diff --git a/app/Http/Controllers/AboutController.php b/app/Http/Controllers/AboutController.php index 2a63ceae..f42dfac0 100644 --- a/app/Http/Controllers/AboutController.php +++ b/app/Http/Controllers/AboutController.php @@ -18,8 +18,6 @@ */ class AboutController extends Controller { - public const LOGO = '/logo.png'; - public function __invoke(ContentRepository $content, SiteFacts $facts): Response { $locale = App::getLocale(); @@ -34,23 +32,6 @@ public function __invoke(ContentRepository $content, SiteFacts $facts): Response ], 'links' => $facts->links(), 'organizationProfiles' => $facts->organizationProfiles(), - 'logo' => $this->logo(), ]); } - - /** - * The logo file the brand block offers, or null when `public/` has none. - * - * @return array{src: string, width: int, height: int}|null - */ - private function logo(): ?array - { - $size = @getimagesize(public_path(ltrim(self::LOGO, '/'))); - - if (! is_array($size)) { - return null; - } - - return ['src' => self::LOGO, 'width' => $size[0], 'height' => $size[1]]; - } } diff --git a/resources/data/content/de/about.json b/resources/data/content/de/about.json index 27e1aef9..2dbd76ae 100644 --- a/resources/data/content/de/about.json +++ b/resources/data/content/de/about.json @@ -46,9 +46,6 @@ }, "brand": { "title": "Marke", - "name": "Der Name wird TablePro geschrieben: ein Wort, mit großem T und großem P.", - "usage": "Du darfst den Namen TablePro und das Logo verwenden, um auf TablePro zu verweisen, etwa in Artikeln, Tutorials, Testberichten, Verzeichnissen und Integrationen. Du darfst das Logo nicht verändern, nicht für ein anderes Produkt verwenden und nicht den Eindruck erwecken, dass TablePro dich empfiehlt. Für alles andere schreib eine E-Mail an {email}.", - "logo": "Logo herunterladen (PNG, {width} × {height})", - "alt": "TablePro-Logo" + "body": "Die Markenrichtlinien erklären, wie du den Namen und das Logo von TablePro verwendest, und enthalten die Logodateien zum Herunterladen." } } diff --git a/resources/data/content/en/about.json b/resources/data/content/en/about.json index 93925d26..4005520a 100644 --- a/resources/data/content/en/about.json +++ b/resources/data/content/en/about.json @@ -46,9 +46,6 @@ }, "brand": { "title": "Brand", - "name": "The name is written TablePro: one word, with a capital T and a capital P.", - "usage": "You may use the TablePro name and logo to refer to TablePro, for example in articles, tutorials, reviews, listings and integrations. You may not alter the logo, use it for another product, or suggest that TablePro endorses you. For anything else, email {email}.", - "logo": "Download the logo (PNG, {width} × {height})", - "alt": "TablePro logo" + "body": "The brand guidelines explain how to use the TablePro name and logo, and have the logo files to download." } } diff --git a/resources/data/content/es/about.json b/resources/data/content/es/about.json index 94e56432..77f5dcb5 100644 --- a/resources/data/content/es/about.json +++ b/resources/data/content/es/about.json @@ -46,9 +46,6 @@ }, "brand": { "title": "Marca", - "name": "El nombre se escribe TablePro: una sola palabra, con T y P mayúsculas.", - "usage": "Puedes usar el nombre y el logotipo de TablePro para referirte a TablePro, por ejemplo en artículos, tutoriales, reseñas, directorios e integraciones. No puedes modificar el logotipo, usarlo para otro producto ni dar a entender que TablePro te respalda. Para cualquier otro uso, escribe a {email}.", - "logo": "Descargar el logotipo (PNG, {width} × {height})", - "alt": "Logotipo de TablePro" + "body": "Las pautas de marca explican cómo usar el nombre y el logotipo de TablePro, e incluyen los archivos del logotipo para descargar." } } diff --git a/resources/data/content/fr/about.json b/resources/data/content/fr/about.json index 5e0ced43..c14bd2a4 100644 --- a/resources/data/content/fr/about.json +++ b/resources/data/content/fr/about.json @@ -46,9 +46,6 @@ }, "brand": { "title": "Marque", - "name": "Le nom s’écrit TablePro, en un seul mot, avec un T et un P majuscules.", - "usage": "Vous pouvez utiliser le nom et le logo TablePro pour parler de TablePro, par exemple dans des articles, des tutoriels, des tests, des annuaires et des intégrations. Vous ne pouvez pas modifier le logo, l’utiliser pour un autre produit ni laisser entendre que TablePro vous recommande. Pour toute autre utilisation, écrivez à {email}.", - "logo": "Télécharger le logo (PNG, {width} × {height})", - "alt": "Logo de TablePro" + "body": "La charte de marque explique comment utiliser le nom et le logo TablePro, et propose les fichiers du logo à télécharger." } } diff --git a/resources/data/content/id/about.json b/resources/data/content/id/about.json index 37dd3653..dc4bd534 100644 --- a/resources/data/content/id/about.json +++ b/resources/data/content/id/about.json @@ -46,9 +46,6 @@ }, "brand": { "title": "Merek", - "name": "Nama ini ditulis TablePro: satu kata, dengan huruf T dan P kapital.", - "usage": "Anda boleh memakai nama dan logo TablePro untuk merujuk ke TablePro, misalnya di artikel, tutorial, ulasan, direktori aplikasi, dan integrasi. Anda tidak boleh mengubah logo, memakainya untuk produk lain, atau mengesankan bahwa TablePro mendukung Anda. Untuk keperluan lain, kirim email ke {email}.", - "logo": "Unduh logo (PNG, {width} × {height})", - "alt": "Logo TablePro" + "body": "Pedoman merek menjelaskan cara memakai nama dan logo TablePro, beserta file logo untuk diunduh." } } diff --git a/resources/data/content/it/about.json b/resources/data/content/it/about.json index ae2fd0b7..1a6c2fff 100644 --- a/resources/data/content/it/about.json +++ b/resources/data/content/it/about.json @@ -46,9 +46,6 @@ }, "brand": { "title": "Marchio", - "name": "Il nome si scrive TablePro, in una sola parola, con la T e la P maiuscole.", - "usage": "Puoi usare il nome e il logo di TablePro per fare riferimento a TablePro, ad esempio in articoli, tutorial, recensioni, directory e integrazioni. Non puoi modificare il logo, usarlo per un altro prodotto o lasciar intendere un sostegno da parte di TablePro. Per qualsiasi altro uso, scrivi a {email}.", - "logo": "Scarica il logo (PNG, {width} × {height})", - "alt": "Logo di TablePro" + "body": "Le linee guida del marchio spiegano come usare il nome e il logo di TablePro e includono i file del logo da scaricare." } } diff --git a/resources/data/content/ja/about.json b/resources/data/content/ja/about.json index 34e50378..c18a7e43 100644 --- a/resources/data/content/ja/about.json +++ b/resources/data/content/ja/about.json @@ -46,9 +46,6 @@ }, "brand": { "title": "ブランド", - "name": "名前は TablePro と表記します。1 語で、T と P は大文字です。", - "usage": "TablePro に言及するためであれば、記事、チュートリアル、レビュー、ソフトウェア一覧、連携機能などで TablePro の名前とロゴを使用できます。ロゴの改変、ほかの製品での使用、TablePro が推薦しているかのような表示はできません。それ以外の用途については {email} までご連絡ください。", - "logo": "ロゴをダウンロード(PNG、{width} × {height})", - "alt": "TablePro のロゴ" + "body": "TablePro の名前とロゴの使い方、ロゴファイルのダウンロードはブランドガイドラインをご覧ください。" } } diff --git a/resources/data/content/ko/about.json b/resources/data/content/ko/about.json index 7168b38d..0a84962f 100644 --- a/resources/data/content/ko/about.json +++ b/resources/data/content/ko/about.json @@ -46,9 +46,6 @@ }, "brand": { "title": "브랜드", - "name": "이름은 TablePro로 표기합니다. 한 단어로 쓰며 T와 P는 대문자입니다.", - "usage": "TablePro를 언급하는 용도라면 기사, 튜토리얼, 리뷰, 소프트웨어 목록, 연동 기능 등에서 TablePro 이름과 로고를 사용할 수 있습니다. 로고를 변경하거나 다른 제품에 사용하거나 TablePro가 보증하는 것처럼 표시해서는 안 됩니다. 그 밖의 용도는 {email}로 문의해 주세요.", - "logo": "로고 다운로드(PNG, {width} × {height})", - "alt": "TablePro 로고" + "body": "TablePro 이름과 로고의 사용 방법, 로고 파일 다운로드는 브랜드 가이드라인에서 확인하세요." } } diff --git a/resources/data/content/pt-BR/about.json b/resources/data/content/pt-BR/about.json index 0aaac31f..ad6b6fbc 100644 --- a/resources/data/content/pt-BR/about.json +++ b/resources/data/content/pt-BR/about.json @@ -46,9 +46,6 @@ }, "brand": { "title": "Marca", - "name": "O nome se escreve TablePro, em uma só palavra, com T e P maiúsculos.", - "usage": "Você pode usar o nome e o logo do TablePro para se referir ao TablePro, por exemplo em artigos, tutoriais, análises, diretórios e integrações. Não é permitido alterar o logo, usá-lo em outro produto ou sugerir que o TablePro endossa você. Para qualquer outro uso, escreva para {email}.", - "logo": "Baixar o logo (PNG, {width} × {height})", - "alt": "Logo do TablePro" + "body": "As diretrizes de marca explicam como usar o nome e o logo do TablePro e trazem os arquivos do logo para baixar." } } diff --git a/resources/data/content/vi/about.json b/resources/data/content/vi/about.json index 9f7d84a8..e2257268 100644 --- a/resources/data/content/vi/about.json +++ b/resources/data/content/vi/about.json @@ -46,9 +46,6 @@ }, "brand": { "title": "Thương hiệu", - "name": "Tên được viết là TablePro: một từ, chữ T và chữ P viết hoa.", - "usage": "Bạn có thể dùng tên và logo TablePro để nói đến TablePro, chẳng hạn trong bài viết, hướng dẫn, bài đánh giá, danh mục phần mềm và các tích hợp. Bạn không được sửa logo, dùng logo cho một sản phẩm khác, hay ngụ ý rằng TablePro bảo chứng cho bạn. Trường hợp khác, bạn hãy gửi email tới {email}.", - "logo": "Tải về logo (PNG, {width} × {height})", - "alt": "Logo TablePro" + "body": "Hướng dẫn thương hiệu giải thích cách dùng tên và logo TablePro, kèm các file logo để tải về." } } diff --git a/resources/data/content/zh-Hans/about.json b/resources/data/content/zh-Hans/about.json index 48f591ba..e168aefb 100644 --- a/resources/data/content/zh-Hans/about.json +++ b/resources/data/content/zh-Hans/about.json @@ -46,9 +46,6 @@ }, "brand": { "title": "品牌", - "name": "名称写作 TablePro:一个词,T 和 P 大写。", - "usage": "您可以在文章、教程、评测、软件目录和集成中使用 TablePro 的名称和徽标来指代 TablePro。请勿修改徽标、将其用于其他产品,或暗示 TablePro 为您背书。其他用途请发送邮件至 {email}。", - "logo": "下载徽标(PNG,{width} × {height})", - "alt": "TablePro 徽标" + "body": "品牌指南说明了如何使用 TablePro 的名称和徽标,并提供徽标文件下载。" } } diff --git a/resources/data/content/zh-Hant/about.json b/resources/data/content/zh-Hant/about.json index 03cb6283..2f97f756 100644 --- a/resources/data/content/zh-Hant/about.json +++ b/resources/data/content/zh-Hant/about.json @@ -46,9 +46,6 @@ }, "brand": { "title": "品牌", - "name": "名稱寫作 TablePro:一個詞,T 與 P 大寫。", - "usage": "您可以在文章、教學、評測、軟體目錄和整合中使用 TablePro 的名稱和標誌來指稱 TablePro。請勿修改標誌、將其用於其他產品,或暗示 TablePro 為您背書。其他用途請寄送郵件至 {email}。", - "logo": "下載標誌(PNG,{width} × {height})", - "alt": "TablePro 標誌" + "body": "品牌指南說明了如何使用 TablePro 的名稱和標誌,並提供標誌檔案下載。" } } diff --git a/resources/js/components/faq/content-links.tsx b/resources/js/components/faq/content-links.tsx index 511c02b8..00777bab 100644 --- a/resources/js/components/faq/content-links.tsx +++ b/resources/js/components/faq/content-links.tsx @@ -75,8 +75,9 @@ const EXTERNAL: Record = { /** * Renderers for every tag FAQ, iPhone-page and about-page copy may use, for ``: * the link tags above, `` (the account app, in the reader's - * language), `` (the support address; its text is the address unless - * the copy gives other words) and `` (an app label in a click path). + * language), `` (the brand guidelines, in English only), `` + * (the support address; its text is the address unless the copy gives other + * words) and `` (an app label in a click path). * * A tag whose destination the data does not name renders its words as plain * text, so a missing URL never becomes a broken link. A link to the English @@ -99,6 +100,14 @@ export function useContentTags(links: SiteLinks): Record ( + <> + + {text} + + {locale !== 'en' && ` ${m.common.englishOnly}`} + + ), }; for (const [name, path] of Object.entries(INTERNAL)) { diff --git a/resources/js/pages/About.tsx b/resources/js/pages/About.tsx index d77b9072..7278a861 100644 --- a/resources/js/pages/About.tsx +++ b/resources/js/pages/About.tsx @@ -20,8 +20,6 @@ interface AboutProps { repositoryCreated: { date: string; formatted: string } | null; links: SiteLinks; organizationProfiles: string[]; - /** The logo file under public/, or null when there is none to offer. */ - logo: { src: string; width: number; height: number } | null; } interface JsonLdInput { @@ -57,12 +55,12 @@ function aboutJsonLd({ baseUrl, inLanguage, pageUrl, content, crumbs, descriptio /** * `/about` in every locale: who makes TablePro and where, how it is funded, - * where the code is, how to reach the maker, and the brand files. + * where the code is, how to reach the maker, and where the brand guidelines are. * * The name, city and country fill `{maker}`, `{city}` and `{country}` from * the `publisher` prop; no sentence holds them. */ -export default function About({ content, publisher, repositoryCreated, links, organizationProfiles, logo }: AboutProps) { +export default function About({ content, publisher, repositoryCreated, links, organizationProfiles }: AboutProps) { const { canonicalBaseUrl } = usePage().props; const { locale, m, fmt, path } = useI18n(); const tags = useContentTags(links); @@ -72,7 +70,6 @@ export default function About({ content, publisher, repositoryCreated, links, or ...(publisher?.city && { city: publisher.city }), ...(publisher?.country && { country: publisher.country }), ...(links.email !== null && { email: links.email }), - ...(logo !== null && { width: logo.width, height: logo.height }), }; const rich = (text: string): ReactNode => ; @@ -160,22 +157,11 @@ export default function About({ content, publisher, repositoryCreated, links, or - {logo !== null && ( -
-
-
-

{content.brand.name}

-

{rich(content.brand.usage)}

-
- -
-
- )} +
+
+

{rich(content.brand.body)}

+
+
); } diff --git a/tests/Feature/Landing/AboutPageTest.php b/tests/Feature/Landing/AboutPageTest.php index 14126998..ef4c06da 100644 --- a/tests/Feature/Landing/AboutPageTest.php +++ b/tests/Feature/Landing/AboutPageTest.php @@ -50,7 +50,7 @@ function aboutKnownTags(): array preg_match('/const EXTERNAL: Record = \{(.*?)\n\};/s', $source, $external); preg_match_all('/^\s+([a-zA-Z]+):/m', ($internal[1] ?? '') . "\n" . ($external[1] ?? ''), $keys); - return [...$keys[1], 'ui', 'account', 'email']; + return [...$keys[1], 'ui', 'account', 'email', 'brand']; } function aboutVisibleText(string $html): string @@ -81,7 +81,6 @@ function aboutVisibleText(string $html): string ->where('links.issues', fn(string $url): bool => str_starts_with($url, 'https://github.com/')) ->where('links.discussions', fn(string $url): bool => str_starts_with($url, 'https://github.com/')) ->where('links.sponsorsProgram', 'https://github.com/sponsors/datlechin') - ->where('logo', ['src' => '/logo.png', 'width' => 256, 'height' => 256]) ->where('seo.robots', 'index, follow')); })->with(aboutLocales()); @@ -99,10 +98,10 @@ function aboutVisibleText(string $html): string preg_match_all('/\{([A-Za-z][A-Za-z0-9_.]*)\}/', $text, $slots); preg_match_all('/<([a-z][A-Za-z0-9]*)>/', $text, $tags); - expect(array_values(array_diff(array_unique($slots[1]), ['maker', 'city', 'country', 'email', 'width', 'height'])))->toBe([], "content/{$locale}/about.json uses a slot the page does not fill"); + expect(array_values(array_diff(array_unique($slots[1]), ['maker', 'city', 'country', 'email'])))->toBe([], "content/{$locale}/about.json uses a slot the page does not fill"); expect(array_values(array_diff(array_unique($tags[1]), aboutKnownTags())))->toBe([], "content/{$locale}/about.json uses a link tag with no destination"); expect($content['policies']['body'])->toMatch('#[^<]+#u'); - expect($content['brand']['usage'] ?? null)->toBeString()->toContain('TablePro')->toContain('{email}'); + expect($content['brand']['body'])->toMatch('#[^<]+#u'); })->with(aboutLocales()); it('types the publisher in facts.json only', function (): void { @@ -147,14 +146,15 @@ function aboutVisibleText(string $html): string } Assert::assertDoesNotMatchRegularExpression('/\{[a-zA-Z]+\}/', $text, "{$path} leaves a slot unfilled"); - expect($html)->toContain('href="/logo.png" download=""'); preg_match('#]*id="policies".*?#s', $html, $policies); preg_match('#]*id="brand".*?#s', $html, $brand); preg_match('##s', $html, $footer); + preg_match('/[\'"]?englishOnly[\'"]?:\s*([\'"])(.*?)\1/u', File::get(resource_path("js/i18n/messages/{$locale}/common.ts")), $marker); expect($policies[0] ?? '')->toContain("href=\"{$prefix}/security\""); - expect($brand[0] ?? '')->toContain('href="mailto:hello@tablepro.app"'); + expect($brand[0] ?? '')->toContain('href="/brand" hrefLang="en"'); + expect(str_contains(aboutVisibleText($brand[0] ?? ''), $marker[2]))->toBe($locale !== Locales::default()); expect(aboutVisibleText($footer[0] ?? ''))->toContain($publisher['name']) ->toContain($publisher['city'][$locale]) ->toContain('AGPLv3');