diff --git a/app/Http/Controllers/IntegrationController.php b/app/Http/Controllers/IntegrationController.php new file mode 100644 index 00000000..4d2079b3 --- /dev/null +++ b/app/Http/Controllers/IntegrationController.php @@ -0,0 +1,337 @@ +, platforms: list, closedSource: bool, tagline: string, keywords: list, icon: Icon|null} + * @phpstan-type Filters array{q: string|null, category: string|null, platform: string|null, tier: string|null} + */ +class IntegrationController extends Controller +{ + private const array TIERS = ['official', 'partner', 'community']; + + // The registry names the OS a TablePro app runs on; the site names the app. + private const array PLATFORMS = ['macos' => 'mac', 'ios' => 'ios', 'ipados' => 'ios']; + + private const array NETWORK = ['none', 'local', 'internet']; + + private const array PAYMENT = ['free', 'optional', 'paid']; + + private const int QUERY_LENGTH = 100; + + private const string IMAGES = '/images/integrations/'; + + public function index(Request $request, ContentRepository $content, IntegrationCatalog $catalog): Response + { + $locale = App::getLocale(); + $page = $content->page('integrations/index', $locale); + $listed = $this->listed($catalog, $page, $content, $locale); + + return Inertia::render('Integrations/Index', [ + 'content' => Arr::except($page, ['taglines', 'show']), + 'integrations' => $listed, + 'filters' => $this->filters($request, $listed), + ]); + } + + public function show(ContentRepository $content, IntegrationCatalog $catalog, string $slug): Response + { + $integration = $catalog->find($slug); + + abort_if($integration === null, 404); + + $locale = App::getLocale(); + $page = $content->page('integrations/index', $locale); + + return Inertia::render('Integrations/Show', [ + 'content' => Arr::only($page, ['labels', 'show']), + 'integration' => $this->detail($integration, $page, $catalog), + 'dates' => [ + 'added' => $this->date($integration['addedAt'], $locale), + 'verified' => $this->date($integration['lastVerifiedAt'], $locale), + 'archived' => $this->date($integration['status']['since'] ?? null, $locale), + ], + ]); + } + + /** + * @param array $page + * @return list + */ + private function listed(IntegrationCatalog $catalog, array $page, ContentRepository $content, string $locale): array + { + $default = $locale === Locales::default(); + $taglines = $this->taglines($page); + $sources = $default ? [] : $this->taglines($content->page('integrations/index', Locales::default())); + $categories = $this->labelled($page, 'labels.categories'); + $listed = []; + + foreach ($catalog->all() as $integration) { + $slug = $integration['slug']; + + if (($integration['status']['state'] ?? null) !== 'active') { + continue; + } + + // Another language lists an entry only while its tagline there was translated from the current summary. + if ($default) { + $tagline = $integration['summary']; + } elseif (($sources[$slug] ?? null) === $integration['summary'] && isset($taglines[$slug])) { + $tagline = $taglines[$slug]; + } else { + continue; + } + + $listed[] = [ + 'slug' => $slug, + 'name' => $integration['name'], + 'publisher' => $integration['publisher']['name'], + 'tier' => $this->tier($integration), + 'categories' => $this->known($integration['categories'], $categories), + 'platforms' => $this->platforms($integration['platforms']), + 'closedSource' => $integration['closedSource'] === true, + 'tagline' => $tagline, + 'keywords' => array_values(array_filter($integration['keywords'] ?? [], 'is_string')), + 'icon' => $this->image($integration['icon']['128'] ?? null), + ]; + } + + $rank = array_flip(self::TIERS); + + usort($listed, fn(array $a, array $b): int => [$rank[$a['tier']], strtolower($a['name'])] <=> [$rank[$b['tier']], strtolower($b['name'])]); + + return $listed; + } + + /** + * @param array $page + * @return array + */ + private function taglines(array $page): array + { + $taglines = is_array($page['taglines'] ?? null) ? $page['taglines'] : []; + + return array_filter($taglines, fn(mixed $tagline): bool => is_string($tagline) && trim($tagline) !== ''); + } + + /** + * @param list $listed + * @return Filters + */ + private function filters(Request $request, array $listed): array + { + $value = function (string $key, string $field) use ($request, $listed): ?string { + $value = $request->query($key); + $present = array_merge(...array_map(fn(array $entry): array => (array) $entry[$field], $listed)); + + return is_string($value) && in_array($value, $present, true) ? $value : null; + }; + + $q = $request->query('q'); + $q = is_string($q) ? trim(mb_substr(trim($q), 0, self::QUERY_LENGTH)) : ''; + + return [ + 'q' => $q !== '' ? $q : null, + 'category' => $value('category', 'categories'), + 'platform' => $value('platform', 'platforms'), + 'tier' => $value('tier', 'tier'), + ]; + } + + /** + * @param array $integration + * @param array $page + * @return array + */ + private function detail(array $integration, array $page, IntegrationCatalog $catalog): array + { + $status = $integration['status']; + $disclosures = $integration['disclosures']; + $install = $integration['install']; + $host = $integration['host'] ?? null; + $versions = $integration['minTableProVersion'] ?? []; + $replacement = is_string($status['replacement'] ?? null) ? $catalog->find($status['replacement']) : null; + $type = in_array($install['type'], $this->labelled($page, 'show.install'), true) ? $install['type'] : 'page'; + $data = $this->labelled($page, 'show.uses.data'); + + return [ + 'slug' => $integration['slug'], + 'name' => $integration['name'], + 'summary' => $integration['summary'], + 'description' => array_values(array_filter(array_map('trim', preg_split('/\n\s*\n/', $integration['description']) ?: []))), + 'tier' => $this->tier($integration), + 'archived' => $status['state'] !== 'active' ? [ + 'reason' => in_array($status['reason'] ?? null, $this->labelled($page, 'show.archived.reasons'), true) ? $status['reason'] : null, + 'note' => $this->text($status['note'] ?? null), + 'replacement' => $replacement !== null ? ['slug' => $replacement['slug'], 'name' => $replacement['name']] : null, + ] : null, + 'publisher' => ['name' => $integration['publisher']['name'], 'url' => $this->https($integration['publisher']['url'] ?? null)], + 'categories' => $this->known($integration['categories'], $this->labelled($page, 'labels.categories')), + 'platforms' => $this->platforms($integration['platforms']), + 'versions' => [ + 'mac' => $this->text($versions['macos'] ?? null), + 'ios' => $this->text($versions['ios'] ?? $versions['ipados'] ?? null), + ], + 'host' => is_array($host) ? [ + 'name' => $host['name'], + 'url' => $this->https($host['url'] ?? null), + 'minVersion' => $this->text($host['minVersion'] ?? null), + 'note' => $this->text($host['note'] ?? null), + ] : null, + 'closedSource' => $integration['closedSource'] === true, + 'license' => $this->text($integration['license'] ?? null), + 'source' => $this->https($integration['source']['url'] ?? null), + 'install' => [ + 'type' => $type, + 'url' => $this->https($install['url']), + 'command' => $type === 'homebrew' ? $this->brew($install) : null, + ], + 'surfaces' => $this->known($integration['surfaces'], $this->labelled($page, 'show.uses.surface')), + 'reads' => $this->known($disclosures['reads'] ?? [], $data), + 'writes' => $this->known($disclosures['writes'] ?? [], $data), + 'accessNote' => $this->text($disclosures['accessNote'] ?? null), + 'lowersSafeMode' => ($disclosures['lowersSafeMode'] ?? false) === true, + 'safeModeNote' => $this->text($disclosures['safeModeNote'] ?? null), + 'network' => in_array($disclosures['network'] ?? null, self::NETWORK, true) ? $disclosures['network'] : null, + 'networkNote' => $this->text($disclosures['networkNote'] ?? null), + 'account' => ($disclosures['account'] ?? false) === true, + 'payment' => in_array($disclosures['payment'] ?? null, self::PAYMENT, true) ? $disclosures['payment'] : null, + 'paymentNote' => $this->text($disclosures['paymentNote'] ?? null), + 'links' => [ + 'docs' => $this->https($integration['links']['docs'] ?? null), + 'issues' => $this->https($integration['links']['issues'] ?? $integration['links']['support'] ?? null), + 'privacy' => $this->https($integration['links']['privacy'] ?? null), + ], + 'icon' => $this->image($integration['icon']['128'] ?? null), + 'screenshots' => $this->screenshots($integration['screenshots']), + ]; + } + + /** + * @param list $screenshots + * @return list + */ + private function screenshots(array $screenshots): array + { + $shots = []; + + foreach ($screenshots as $shot) { + $image = $this->image($shot); + + if ($image !== null && is_string($shot['alt'] ?? null)) { + $shots[] = [...$image, 'alt' => $shot['alt']]; + } + } + + return $shots; + } + + /** + * @param array $integration + */ + private function tier(array $integration): string + { + // An unknown tier gets the most cautious treatment: the disclaimer and rel="ugc nofollow". + return in_array($integration['tier'], self::TIERS, true) ? $integration['tier'] : 'community'; + } + + /** + * @param list $platforms + * @return list + */ + private function platforms(array $platforms): array + { + $apps = array_map(fn(mixed $platform): ?string => is_string($platform) ? (self::PLATFORMS[$platform] ?? null) : null, $platforms); + + return array_values(array_intersect(array_unique(self::PLATFORMS), $apps)); + } + + /** + * @param array $page + * @return list + */ + private function labelled(array $page, string $key): array + { + // A value the registry adds later stays off the page until the content file labels it. + $labels = Arr::get($page, $key); + + return is_array($labels) ? array_map('strval', array_keys($labels)) : []; + } + + /** + * @param list $values + * @param list $known + * @return list + */ + private function known(array $values, array $known): array + { + return array_values(array_intersect($known, $values)); + } + + /** + * @return Icon|null + */ + private function image(mixed $image): ?array + { + if (! is_array($image) || ! is_string($image['file'] ?? null) || preg_match('/^[a-z0-9]+(?:-[a-z0-9]+)*-[0-9a-f]{12}\.png$/', $image['file']) !== 1) { + return null; + } + + return ['src' => self::IMAGES . $image['file'], 'width' => (int) $image['width'], 'height' => (int) $image['height']]; + } + + /** + * @param array $install + */ + private function brew(array $install): ?string + { + $token = $install['token'] ?? null; + + if (! is_string($token) || preg_match('/^[a-z0-9][a-z0-9@+._-]{0,99}$/', $token) !== 1) { + return null; + } + + return ($install['kind'] ?? null) === 'cask' ? "brew install --cask {$token}" : "brew install {$token}"; + } + + private function https(mixed $url): ?string + { + if (! is_string($url) || ! str_starts_with($url, 'https://') || filter_var($url, FILTER_VALIDATE_URL) === false) { + return null; + } + + return parse_url($url, PHP_URL_USER) === null ? $url : null; + } + + private function text(mixed $value): ?string + { + return is_string($value) && trim($value) !== '' ? $value : null; + } + + private function date(mixed $value, string $locale): ?string + { + if (! is_string($value) || preg_match('/^\d{4}-\d{2}-\d{2}$/', $value) !== 1) { + return null; + } + + try { + $date = Carbon::createFromFormat('!Y-m-d', $value); + } catch (InvalidArgumentException) { + return null; + } + + return $date !== null && $date->format('Y-m-d') === $value ? $date->locale($locale)->isoFormat('LL') : null; + } +} diff --git a/app/Providers/SeoServiceProvider.php b/app/Providers/SeoServiceProvider.php index ae0e7ef7..36eff356 100644 --- a/app/Providers/SeoServiceProvider.php +++ b/app/Providers/SeoServiceProvider.php @@ -5,6 +5,7 @@ use App\Support\Content\ContentRepository; use App\Support\Seo\BlogPosts; use App\Support\Seo\ContentCollection; +use App\Support\Seo\IntegrationPages; use App\Support\Seo\LastModified; use App\Support\Seo\LegalPages; use App\Support\Seo\OgFonts; @@ -29,6 +30,7 @@ * 2. `ContentCollection`: features, databases and comparisons, with their hubs. * 3. `LegalPages`: privacy, terms, refund policy. * 4. `BlogPosts`: one page per post, per language it is written in. + * 5. `IntegrationPages`: one English page per entry in `integrations.json`. * * Every family is wrapped in `WithoutRetiredPaths`, so a URL the redirect map * answers is never a page, whichever family still lists it. @@ -69,6 +71,7 @@ public function register(): void new ContentCollection($content), $app->make(LegalPages::class), $app->make(BlogPosts::class), + $app->make(IntegrationPages::class), ]; return new PageRegistry(array_map( diff --git a/app/Support/Localization/LocaleSwitcher.php b/app/Support/Localization/LocaleSwitcher.php index 09f47473..ee5df7bc 100644 --- a/app/Support/Localization/LocaleSwitcher.php +++ b/app/Support/Localization/LocaleSwitcher.php @@ -36,6 +36,7 @@ final class LocaleSwitcher 'compare' => 'landing.compare.index', 'database' => 'landing.databases.index', 'feature' => 'landing.features.index', + 'integration' => 'landing.integrations.index', ]; public function __construct( diff --git a/app/Support/Seo/ContentCollection.php b/app/Support/Seo/ContentCollection.php index 582f6a0c..e0b5a2d9 100644 --- a/app/Support/Seo/ContentCollection.php +++ b/app/Support/Seo/ContentCollection.php @@ -10,7 +10,7 @@ /** * The page families built from content collections: features, databases and - * comparisons, each with its hub. + * comparisons, each with its hub, and the integrations hub. * * A page renders in a locale when `resources/data/content/{locale}/{family}/{slug}.json` * exists (`index.json` for a hub), and is indexable there unless that file @@ -37,6 +37,7 @@ final class ContentCollection implements PageFamily 'landing.features.index' => ['features', 'feature', ['resources/data/paid-features.json', 'resources/data/facts.json']], 'landing.databases.index' => ['databases', 'database', ['resources/data/engines.json']], 'landing.compare.index' => ['compare', 'compare', ['resources/data/comparisons.json']], + 'landing.integrations.index' => ['integrations', 'integration', ['resources/data/integrations.json']], ]; /** diff --git a/app/Support/Seo/IntegrationPages.php b/app/Support/Seo/IntegrationPages.php new file mode 100644 index 00000000..938e62df --- /dev/null +++ b/app/Support/Seo/IntegrationPages.php @@ -0,0 +1,63 @@ + + */ + public function entries(): array + { + $entries = []; + + foreach ($this->catalog->all() as $integration) { + $entry = $this->find(self::ROUTE, ['slug' => $integration['slug']]); + + if ($entry !== null) { + $entries[] = $entry; + } + } + + return $entries; + } + + /** + * @param array $params + */ + public function find(string $route, array $params): ?PageEntry + { + $slug = $params['slug'] ?? null; + + if ($route !== self::ROUTE || ! is_string($slug) || count($params) !== 1 || ! BlogPosts::isSlug($slug)) { + return null; + } + + $integration = $this->catalog->find($slug); + + if ($integration === null) { + return null; + } + + $locale = Locales::default(); + + return new PageEntry( + route: self::ROUTE, + params: ['slug' => $slug], + renderLocales: [$locale], + indexableLocales: ($integration['status']['state'] ?? null) === 'active' ? [$locale] : [], + sources: ['resources/data/integrations.json', "resources/data/content/{$locale}/integrations/index.json"], + ogFamily: 'integration', + ogSlug: null, + ); + } +} diff --git a/app/Support/Seo/PageEntry.php b/app/Support/Seo/PageEntry.php index e5185cd2..617cb068 100644 --- a/app/Support/Seo/PageEntry.php +++ b/app/Support/Seo/PageEntry.php @@ -22,7 +22,7 @@ * @param list $renderLocales locales in which the URL answers 200 * @param list $indexableLocales subset of $renderLocales: index, sitemap, hreflang * @param list $sources repository paths whose last change is the page's lastmod - * @param string $ogFamily site | blog | database | compare | feature + * @param string $ogFamily site | blog | database | compare | feature | integration */ public function __construct( public string $route, diff --git a/docs/architecture.md b/docs/architecture.md index d34785ee..33aebb1b 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -12,6 +12,7 @@ database and no credentials: every page is built from markdown in /features /features/{slug} /databases /{database}-client /{database}-gui /compare /compare/{slug} +/integrations /integrations/{slug} from resources/data/integrations.json; detail pages in English only /privacy /terms /refund-policy /brand English only: resources/data/legal/en/brand.md, files in public/images/brand /blog /blog/{slug} markdown in resources/blog @@ -103,11 +104,12 @@ the two copies are compared by its `LanguageRegionsConfigTest`. ``` resources/data/*.json locale-neutral facts: locales, platforms, engines, pricing, paid-features, facts, sponsors, comparisons, - assets, redirects; brand (the logo files, with the - English labels of the English-only /brand) + assets, redirects, integrations; brand (the logo files, + with the English labels of the English-only /brand) resources/data/content/{locale}/ page copy: home, download, ios, pricing, faq, blog, - legal, engines, paid-features, and one file per - page under features/, databases/, compare/ + legal, engines, paid-features, one file per page + under features/, databases/, compare/, and + integrations/index.json resources/data/legal/{locale}/*.md privacy, terms, refund policy; brand guidelines (en only) resources/blog/*.md posts (English); resources/blog/vi/*.md their translations lang/{locale}/*.php OG card labels and the static error pages @@ -126,6 +128,30 @@ A database, comparison or feature page exists when its slug is in the constant class in `app/Support/Content/Slugs/` (the route constraint) and its content file exists in a locale. `LocaleRoutingTest` holds the two lists together. +An integration page is the exception: its route takes any slug-shaped value, +and it exists when `resources/data/integrations.json` has that slug, so a sync +adds or removes pages without a PHP change or a route cache rebuild. + +### Integrations + +`resources/data/integrations.json` and the PNGs in `public/images/integrations/` +are a byte copy of a [TableProApp/integrations](https://github.com/TableProApp/integrations) +release, brought in by the pull request `integrations-sync.yml` opens. Nothing +here edits them. Icons and screenshots are PNG only, so no third-party SVG is +served from this origin, and every text field renders as React text. + +- `/integrations` renders in every locale. English lists every active entry + with the registry's `summary`. Another locale lists an entry only while + `content/{locale}/integrations/index.json` → `taglines` has it and + `content/en/…` → `taglines` still equals the summary, so a changed summary + hides the old translation until it is updated. +- `/integrations/{slug}` renders in English only (`IntegrationPages`), and + archived entries keep their page with `noindex`. Other locales answer 404 + with a link to the English page. +- The hub filters with one function in the server render and the browser + (`components/integrations/model.ts`), so the GET form works without + JavaScript and the canonical stays `/integrations`. + ### Retired URLs Redirects and 410s are not routes. `App\Http\Middleware\CanonicalizeRequest` diff --git a/docs/rebuild/design/design-system.md b/docs/rebuild/design/design-system.md index 018bdde9..dee1680a 100644 --- a/docs/rebuild/design/design-system.md +++ b/docs/rebuild/design/design-system.md @@ -594,6 +594,8 @@ Repo key: | `ComparisonTable` | P | **Replace** | `landing/compare-table.tsx`, Compare page tables | One competitor per page, sourced and dated. Removed from the homepage | | `EngineTable` | P | **Replace** | `landing/database-grid.tsx` (tiles, filter, 2-D arrow keys) | Grouped table on the hub | | `EngineList` | P | **New** | — | Compact named list on the homepage | +| `IntegrationFilters` | P | **New** | — | `/integrations` only: a `role="search"` GET form of native controls (search, category, TablePro app, type). Results update as you type once the page has hydrated | +| `IntegrationList` | P | **New** | — | Integrations as ruled rows, rail to rail: mark, name, publisher, type, tagline. Not a card grid | | `DatabaseMark` | P | **Refactor** | `ui/database-mark.tsx` | Original colours, no `grayscale` / `opacity-40` / `invert`. Uses `iconDark` from data or a light tile | | `SponsorList` | P | **Refactor** | `landing/sponsor-row.tsx` | The 4 verified sponsors only. Each link named | | `ProseArticle` | P | **Refactor** | `.blog-article` CSS, `ui/prose-block.tsx` | Blog and legal. Body in `--foreground`. Permalink glyph kept out of the heading's accessible name | @@ -658,6 +660,7 @@ Repo key: - Opens in the same tab by default and shows `↗` (`aria-hidden`). - Docs links on Vietnamese pages read "Tài liệu (tiếng Anh)" because the docs are English-only. - When `target="_blank"` is unavoidable, add an sr-only "(opens in a new tab)" / "(mở trong thẻ mới)". + - A link from a Community integration's data (install, source, publisher, help) carries `rel="ugc nofollow"`. Official and Partner links carry none. - **Cross-app** (public ↔ `/account`, `/checkout`, `/thank-you`): - Always a plain ``, never an Inertia ``, because the other app answers with its own HTML and asset version. - Locale-aware: `/vi/...` public targets from Vietnamese account screens. @@ -984,6 +987,7 @@ One shared file, `components/shared/theme-control.tsx` (architecture §1.10 and - In dark mode use `iconDark` from data when the vendor publishes an official variant. Otherwise render the mark on a 32px `#ffffff` tile with 6px radius. - Two-letter monogram fallback: `mono` 13 on `--surface`, 1px `--rule`. - Always next to the visible name; the mark is `alt=""`. +- Integration icons follow the same rules, as PNG only: no third-party SVG is served from the site's origin. **SponsorList:** @@ -1769,6 +1773,35 @@ Card (surface) Newsletter Checkbox consent · [Subscribe] secondary · inline re None of these pages is indexed (`noindex`). The single robots tag comes from the page. +### 8.21 Integrations hub (`/integrations`, every locale) + +``` +PageHeader H1 · lead · Build an integration ↗ (docs) + IntegrationFilters: Search · Category · TablePro app · Type · [Show results] +p role=status "{count} integrations" +IntegrationList one row per entry, Official first; or EmptyState "No integrations match" · Clear filters → +#publish How to submit ↗ · Listing policy ↗ (the registry on GitHub) · owners' names and logos line +``` + +- **The one page with search and filters**, by the owner's decision of 2026-10-10. §8.9's "no filter" rule still holds for the blog. +- One filter function serves the server render and the browser. Without JavaScript the form reloads the page with the query; with it, results follow each change and the address bar is replaced, never pushed. The canonical stays `/integrations`. +- Another language lists an entry only once its tagline there is translated from the current English summary. Each row links the English detail page with "(English)". + +### 8.22 Integration page (`/integrations/{slug}`, English only) + +``` +Breadcrumbs Integrations / {name} +cols 1-8 mark 40 · H1 {name} · lead {summary} · By {publisher} · type Badge · Closed source · Archived + Callout warning when archived · install action by type (button ↗, or a Homebrew CodeBlock) · Source code ↗ +cols 9-12 Card "Details": Publisher · Category · Works with · TablePro app · License · Listed · Last checked +#about description paragraphs, as text +#screenshots CellGrid, 2 columns from 768 +#uses "What it uses": TablePro features (docs ↗) · Reads · Changes · Callouts for lowered Safe Mode, internet, account, cost +#support Report a problem ↗ (registry issue form) · Get help from {publisher} ↗ · Documentation ↗ · Callout when not Official +``` + +`/vi/integrations/{slug}` and the other languages answer 404 with a link to the English page. + --- ## 9. Sharing between the two repositories diff --git a/docs/rebuild/design/positioning.md b/docs/rebuild/design/positioning.md index 170cd3e0..7a0b7ce8 100644 --- a/docs/rebuild/design/positioning.md +++ b/docs/rebuild/design/positioning.md @@ -531,7 +531,7 @@ move to their hubs, so no page becomes undiscoverable. There is no footer blurb. | Group EN / VI | Links EN | Links VI | Targets | |---|---|---|---| -| Product / Sản phẩm | Features · Databases · iPhone & iPad · Pricing · Download · Compare | Tính năng · Cơ sở dữ liệu · iPhone và iPad · Bảng giá · Tải về · So sánh | `/features`, `/databases`, the platform link (§10.1), `/pricing`, `/download`, `/compare` | +| Product / Sản phẩm | Features · Databases · Integrations · iPhone & iPad · Pricing · Download · Compare | Tính năng · Cơ sở dữ liệu · Tích hợp · iPhone và iPad · Bảng giá · Tải về · So sánh | `/features`, `/databases`, `/integrations`, the platform link (§10.1), `/pricing`, `/download`, `/compare` | | Resources / Tài nguyên | Documentation ↗ · Changelog ↗ · Blog · FAQ · About · Source code ↗ · Report a bug ↗ | Tài liệu (tiếng Anh) ↗ · Changelog (tiếng Anh) ↗ · Blog · Câu hỏi thường gặp · Giới thiệu · Mã nguồn ↗ · Báo lỗi ↗ | Docs, docs `/changelog`, `/blog`, `/faq`, `/about`, the GitHub repository, GitHub issues | | Support / Hỗ trợ | Account · Troubleshooting ↗ · Email support · Live chat | Tài khoản · Khắc phục sự cố (tiếng Anh) ↗ · Gửi email hỗ trợ · Chat trực tuyến | `/account?locale=`; the docs troubleshooting page; `mailto:` the support address in `facts.json`; a button that loads Crisp only when clicked | | Community / Cộng đồng | GitHub Discussions · Discord · X · Sponsor TablePro ↗ | GitHub Discussions · Discord · X · Telegram · Tài trợ TablePro ↗ | `facts.json` `links`. Brand names stay as they are. Telegram is a Vietnamese group, so only Vietnamese pages link it. "Sponsor TablePro" goes to GitHub Sponsors | @@ -643,6 +643,7 @@ toàn" appear. Prose keeps "Safe Mode". |---|---| | Features · Databases · Pricing · Docs · Documentation · Blog · Account · Download | Tính năng · Cơ sở dữ liệu · Bảng giá · Tài liệu · Tài liệu · Blog · Tài khoản · Tải về | | FAQ · Comparisons · Support · Changelog | Câu hỏi thường gặp · So sánh · Hỗ trợ · Changelog | +| Integrations | Tích hợp | | Language · Theme · Light · Dark · System | Ngôn ngữ · Giao diện · Sáng · Tối · Theo hệ thống | | Privacy · Terms · Refund policy · Cookie settings | Quyền riêng tư · Điều khoản sử dụng · Chính sách hoàn tiền · Cài đặt cookie | | (English) label for English-only content | (tiếng Anh) | diff --git a/docs/rebuild/design/sitemap.md b/docs/rebuild/design/sitemap.md index ad5e9415..16d9a40d 100644 --- a/docs/rebuild/design/sitemap.md +++ b/docs/rebuild/design/sitemap.md @@ -306,7 +306,7 @@ The footer replaces the 11 comparison links and 26 database links with hubs, and | Group (EN / VI) | Links | |---|---| -| Product / Sản phẩm | Features · Databases · iPhone & iPad · Pricing · Download · Compare | +| Product / Sản phẩm | Features · Databases · Integrations · iPhone & iPad · Pricing · Download · Compare | | Resources / Tài nguyên | Documentation ↗ (VI: "Tài liệu (tiếng Anh)") · Changelog ↗ (docs `/changelog`) · Blog · FAQ · About · Brand (`/brand` in every language; VI: "Thương hiệu (tiếng Anh)") · Source code ↗ (GitHub) · Report a bug ↗ (GitHub issues) | | Support / Hỗ trợ | Account (`/account?locale=`) · Troubleshooting ↗ (docs `/troubleshooting`) · Email (`hello@tablepro.app`) · Live chat (a button that loads Crisp only on click) | | Community / Cộng đồng | GitHub Discussions · Discord · X · Telegram (Vietnamese pages only: the group is in Vietnamese) · Sponsor TablePro (GitHub Sponsors). The repository is linked once, as "Source code" under Resources | diff --git a/resources/data/content/de/features/ai-mcp.json b/resources/data/content/de/features/ai-mcp.json index 45a6ea1c..94431578 100644 --- a/resources/data/content/de/features/ai-mcp.json +++ b/resources/data/content/de/features/ai-mcp.json @@ -91,6 +91,12 @@ "paragraphs": [ "AppleScript kann sich verbinden, eine Abfrage ausführen und eine Tabelle öffnen, im Rahmen der Stufe für External Clients und des Safe Mode der Verbindung. tablepro://-Links öffnen eine Verbindung, eine Tabelle oder einen Abfrage-Tab; eine Abfrage in einem Link wird nach deiner Bestätigung eingefügt und nie von selbst ausgeführt. Datenbank-URLs wie postgresql:// öffnen sich als Verbindungen.", "Der Shell-Befehl tablepro öffnet diese URLs und Dateien in der App; Abfragen aus dem Terminal führt er nicht aus. Die offizielle Raycast-Erweiterung durchsucht deine Verbindungen, Tabellen und deinen Abfrageverlauf und führt Abfragen über die gekoppelte App aus." + ], + "links": [ + { + "label": "Integrationen", + "href": "/integrations" + } ] }, { diff --git a/resources/data/content/de/integrations/index.json b/resources/data/content/de/integrations/index.json new file mode 100644 index 00000000..bf3d6611 --- /dev/null +++ b/resources/data/content/de/integrations/index.json @@ -0,0 +1,166 @@ +{ + "seo": { + "title": "Integrationen", + "description": "Apps und Werkzeuge, die mit TablePro zusammenarbeiten: KI-Clients, Editoren, Automatisierung und Befehlszeile, von TablePro oder von anderen entwickelt." + }, + "header": { + "title": "Integrationen", + "lead": "Apps und Werkzeuge, die mit TablePro zusammenarbeiten.", + "build": "Integration entwickeln" + }, + "filters": { + "label": "Integrationen filtern", + "search": "Suchen", + "category": { + "label": "Kategorie", + "all": "Alle Kategorien" + }, + "platform": { + "label": "TablePro-App", + "all": "Alle" + }, + "tier": { + "label": "Typ", + "all": "Alle Typen" + }, + "submit": "Ergebnisse anzeigen", + "count": { + "one": "{count} Integration", + "other": "{count} Integrationen" + }, + "empty": "Keine Integration passt zu diesen Filtern.", + "clear": "Filter zurücksetzen" + }, + "publish": { + "title": "Deine Integration eintragen", + "guide": "Anleitung zum Einreichen", + "policy": "Aufnahmerichtlinie" + }, + "notice": "Namen und Logos gehören ihren Inhabern. Nur offizielle Integrationen werden von TablePro entwickelt.", + "labels": { + "by": "Von {publisher}", + "closedSource": "Nicht quelloffen", + "tiers": { + "official": "Offiziell", + "partner": "Partner", + "community": "Community" + }, + "categories": { + "ai-clients": "KI-Clients", + "automation": "Automatisierung", + "cloud-hosting": "Cloud-Hosting", + "command-line": "Befehlszeile", + "editors": "Editoren", + "launchers": "Launcher", + "local-development": "Lokale Entwicklung", + "other": "Sonstiges" + } + }, + "show": { + "seoTitle": "Integration für {name}", + "install": { + "built-in": "In TablePro einrichten", + "ships-with-host": "Mit {host} einrichten", + "raycast-store": "Aus dem Raycast Store installieren", + "vscode-marketplace": "Aus dem Visual Studio Marketplace installieren", + "github-release": "Von GitHub herunterladen", + "homebrew": "Mit Homebrew installieren", + "app-store": "Im App Store ansehen", + "alfred-gallery": "Aus der Alfred Gallery installieren", + "page": "Zur Seite von {name}" + }, + "source": "Quellcode", + "details": { + "title": "Informationen", + "publisher": "Herausgeber", + "category": "Kategorie", + "host": "Funktioniert mit", + "hostVersion": "{host} {version} oder neuer", + "apps": "TablePro-App", + "appVersion": "{app} {version} oder neuer", + "license": "Lizenz", + "added": "Aufgenommen", + "verified": "Zuletzt geprüft" + }, + "about": "Beschreibung", + "screenshots": "Bildschirmfotos", + "uses": { + "title": "Was die Integration nutzt", + "surfaces": "TablePro-Funktionen", + "reads": "Liest", + "writes": "Ändert", + "none": "Nichts", + "data": { + "connections": "Verbindungsliste", + "schema": "Schemas", + "data": "Tabellendaten" + }, + "surface": { + "url-scheme": { + "label": "tablepro://-Links", + "docs": "/developers/url-scheme" + }, + "mcp": { + "label": "MCP-Server", + "docs": "/features/mcp" + }, + "pairing": { + "label": "Kopplung", + "docs": "/developers/pairing" + }, + "applescript": { + "label": "AppleScript", + "docs": "/developers/applescript" + }, + "database-url": { + "label": "Datenbank-URLs", + "docs": "/connections/urls" + }, + "cli": { + "label": "Befehl tablepro", + "docs": "/integrations/terminal" + }, + "shortcuts": { + "label": "Kurzbefehl-Aktionen", + "docs": "/integrations/ios-shortcuts" + } + }, + "safeMode": "Senkt die Safe-Mode-Stufe", + "safeModeLink": "Safe-Mode-Stufen", + "internet": "Sendet Daten über das Internet", + "account": "Erfordert ein Konto bei einem anderen Dienst", + "privacy": "Datenschutzerklärung", + "payment": { + "optional": "Kostenlos, mit kostenpflichtigen Optionen", + "paid": "Kostenpflichtig" + } + }, + "support": { + "title": "Hilfe", + "report": "Problem mit diesem Eintrag melden", + "help": "Hilfe bei {publisher} erhalten", + "docs": "Dokumentation" + }, + "disclaimer": "{name} wird von {publisher} entwickelt, nicht von TablePro. TablePro führt die Integration hier auf, leistet dafür aber keinen Support.", + "archived": { + "badge": "Archiviert", + "since": "Archiviert am {date}.", + "reasons": { + "unmaintained": "Sie wird nicht mehr gepflegt.", + "broken": "Sie funktioniert nicht mehr mit TablePro.", + "source-archived": "Ihr Quellcode-Repository ist archiviert.", + "superseded": "Sie wurde durch {name} ersetzt.", + "author-request": "Ihr Autor hat um die Archivierung gebeten.", + "policy": "Sie erfüllt die Aufnahmerichtlinie nicht mehr." + } + } + }, + "taglines": { + "claude-code": "Verbinde Claude Code über den in TablePro integrierten MCP-Server mit deinen Datenbanken.", + "claude-desktop": "Verbinde Claude Desktop über den in TablePro integrierten MCP-Server mit deinen Datenbanken.", + "command-line": "Öffne Datenbank-URLs und SQL-Dateien mit dem Befehl tablepro aus dem Terminal in TablePro.", + "cursor": "Verbinde den Agenten von Cursor über den in TablePro integrierten MCP-Server mit deinen Datenbanken.", + "shortcuts": "Füge mit der Kurzbefehle-App auf iPhone und iPad Zeilen in eine Tabelle ein und öffne eine Verbindung.", + "zed": "Verbinde den Agenten von Zed über den in TablePro integrierten MCP-Server mit deinen Datenbanken." + } +} diff --git a/resources/data/content/en/features/ai-mcp.json b/resources/data/content/en/features/ai-mcp.json index d7ae897e..42cf3591 100644 --- a/resources/data/content/en/features/ai-mcp.json +++ b/resources/data/content/en/features/ai-mcp.json @@ -84,7 +84,8 @@ "paragraphs": [ "AppleScript can connect, run a query and open a table, within the connection’s External Clients level and Safe Mode. tablepro:// links open a connection, a table or a query tab; a query in a link is filled in after you confirm and never runs by itself. Database URLs such as postgresql:// open as connections.", "The tablepro shell command opens those URLs and files in the app; it does not run queries from the terminal. The official Raycast extension searches your connections, tables and query history, and runs queries through the paired app." - ] + ], + "links": [{ "label": "Integrations", "href": "/integrations" }] }, { "id": "privacy", diff --git a/resources/data/content/en/integrations/index.json b/resources/data/content/en/integrations/index.json new file mode 100644 index 00000000..a96a090e --- /dev/null +++ b/resources/data/content/en/integrations/index.json @@ -0,0 +1,166 @@ +{ + "seo": { + "title": "Integrations", + "description": "Apps and tools that work with TablePro: AI clients, editors, automation and the command line, built by TablePro or by others." + }, + "header": { + "title": "Integrations", + "lead": "Apps and tools that work with TablePro.", + "build": "Build an integration" + }, + "filters": { + "label": "Filter integrations", + "search": "Search", + "category": { + "label": "Category", + "all": "All categories" + }, + "platform": { + "label": "TablePro app", + "all": "Any" + }, + "tier": { + "label": "Type", + "all": "All types" + }, + "submit": "Show results", + "count": { + "one": "{count} integration", + "other": "{count} integrations" + }, + "empty": "No integrations match these filters.", + "clear": "Clear filters" + }, + "publish": { + "title": "List your integration", + "guide": "How to submit", + "policy": "Listing policy" + }, + "notice": "Names and logos belong to their owners. Only Official integrations are built by TablePro.", + "labels": { + "by": "By {publisher}", + "closedSource": "Closed source", + "tiers": { + "official": "Official", + "partner": "Partner", + "community": "Community" + }, + "categories": { + "ai-clients": "AI clients", + "automation": "Automation", + "cloud-hosting": "Cloud hosting", + "command-line": "Command line", + "editors": "Editors", + "launchers": "Launchers", + "local-development": "Local development", + "other": "Other" + } + }, + "show": { + "seoTitle": "{name} integration", + "install": { + "built-in": "Set up in TablePro", + "ships-with-host": "Set up with {host}", + "raycast-store": "Install from the Raycast Store", + "vscode-marketplace": "Install from the Visual Studio Marketplace", + "github-release": "Download from GitHub", + "homebrew": "Install with Homebrew", + "app-store": "View on the App Store", + "alfred-gallery": "Install from the Alfred Gallery", + "page": "Get {name}" + }, + "source": "Source code", + "details": { + "title": "Details", + "publisher": "Publisher", + "category": "Category", + "host": "Works with", + "hostVersion": "{host} {version} or later", + "apps": "TablePro app", + "appVersion": "{app} {version} or later", + "license": "License", + "added": "Listed", + "verified": "Last checked" + }, + "about": "About", + "screenshots": "Screenshots", + "uses": { + "title": "What it uses", + "surfaces": "TablePro features", + "reads": "Reads", + "writes": "Changes", + "none": "Nothing", + "data": { + "connections": "Connection list", + "schema": "Schemas", + "data": "Table data" + }, + "surface": { + "url-scheme": { + "label": "tablepro:// links", + "docs": "/developers/url-scheme" + }, + "mcp": { + "label": "MCP server", + "docs": "/features/mcp" + }, + "pairing": { + "label": "Pairing", + "docs": "/developers/pairing" + }, + "applescript": { + "label": "AppleScript", + "docs": "/developers/applescript" + }, + "database-url": { + "label": "Database URLs", + "docs": "/connections/urls" + }, + "cli": { + "label": "tablepro command", + "docs": "/integrations/terminal" + }, + "shortcuts": { + "label": "Shortcuts actions", + "docs": "/integrations/ios-shortcuts" + } + }, + "safeMode": "Lowers Safe Mode", + "safeModeLink": "Safe Mode levels", + "internet": "Sends data over the internet", + "account": "Needs an account with another service", + "privacy": "Privacy policy", + "payment": { + "optional": "Free, with paid options", + "paid": "Paid" + } + }, + "support": { + "title": "Help", + "report": "Report a problem with this listing", + "help": "Get help from {publisher}", + "docs": "Documentation" + }, + "disclaimer": "{name} is built by {publisher}, not by TablePro. TablePro lists it here but does not support it.", + "archived": { + "badge": "Archived", + "since": "Archived on {date}.", + "reasons": { + "unmaintained": "It is no longer maintained.", + "broken": "It no longer works with TablePro.", + "source-archived": "Its source repository is archived.", + "superseded": "It was replaced by {name}.", + "author-request": "Its author asked for it to be archived.", + "policy": "It no longer meets the listing policy." + } + } + }, + "taglines": { + "claude-code": "Connect Claude Code to your databases through the MCP server built into TablePro.", + "claude-desktop": "Connect Claude Desktop to your databases through the MCP server built into TablePro.", + "command-line": "Open database URLs and SQL files in TablePro from Terminal with the tablepro command.", + "cursor": "Connect Cursor's agent to your databases through the MCP server built into TablePro.", + "shortcuts": "Add rows to a table and open a connection from Shortcuts on iPhone and iPad.", + "zed": "Connect Zed's agent to your databases through the MCP server built into TablePro." + } +} diff --git a/resources/data/content/es/features/ai-mcp.json b/resources/data/content/es/features/ai-mcp.json index 3fcc21c7..8f41f3fe 100644 --- a/resources/data/content/es/features/ai-mcp.json +++ b/resources/data/content/es/features/ai-mcp.json @@ -91,6 +91,12 @@ "paragraphs": [ "AppleScript puede conectarse, ejecutar una consulta y abrir una tabla, dentro del nivel de External Clients y del Safe Mode de la conexión. Los enlaces tablepro:// abren una conexión, una tabla o una pestaña de consulta; la consulta de un enlace se rellena después de que confirmes y nunca se ejecuta sola. Las URL de bases de datos como postgresql:// se abren como conexiones.", "El comando de shell tablepro abre esas URL y archivos en la app; no ejecuta consultas desde el terminal. La extensión oficial de Raycast busca en tus conexiones, tablas e historial de consultas, y ejecuta consultas a través de la app emparejada." + ], + "links": [ + { + "label": "Integraciones", + "href": "/integrations" + } ] }, { diff --git a/resources/data/content/es/integrations/index.json b/resources/data/content/es/integrations/index.json new file mode 100644 index 00000000..bf7568d3 --- /dev/null +++ b/resources/data/content/es/integrations/index.json @@ -0,0 +1,166 @@ +{ + "seo": { + "title": "Integraciones", + "description": "Apps y herramientas que funcionan con TablePro: clientes de IA, editores, automatización y línea de comandos, de TablePro o de terceros." + }, + "header": { + "title": "Integraciones", + "lead": "Apps y herramientas que funcionan con TablePro.", + "build": "Crear una integración" + }, + "filters": { + "label": "Filtrar integraciones", + "search": "Buscar", + "category": { + "label": "Categoría", + "all": "Todas las categorías" + }, + "platform": { + "label": "App de TablePro", + "all": "Cualquiera" + }, + "tier": { + "label": "Tipo", + "all": "Todos los tipos" + }, + "submit": "Mostrar resultados", + "count": { + "one": "{count} integración", + "other": "{count} integraciones" + }, + "empty": "Ninguna integración coincide con estos filtros.", + "clear": "Borrar filtros" + }, + "publish": { + "title": "Añade tu integración", + "guide": "Cómo enviarla", + "policy": "Política del directorio" + }, + "notice": "Los nombres y logotipos pertenecen a sus propietarios. TablePro solo desarrolla las integraciones de tipo Oficial.", + "labels": { + "by": "Por {publisher}", + "closedSource": "Código cerrado", + "tiers": { + "official": "Oficial", + "partner": "Socio", + "community": "Comunidad" + }, + "categories": { + "ai-clients": "Clientes de IA", + "automation": "Automatización", + "cloud-hosting": "Alojamiento en la nube", + "command-line": "Línea de comandos", + "editors": "Editores", + "launchers": "Lanzadores", + "local-development": "Desarrollo local", + "other": "Otros" + } + }, + "show": { + "seoTitle": "Integración con {name}", + "install": { + "built-in": "Configurar en TablePro", + "ships-with-host": "Configurar con {host}", + "raycast-store": "Instalar desde la Raycast Store", + "vscode-marketplace": "Instalar desde Visual Studio Marketplace", + "github-release": "Descargar desde GitHub", + "homebrew": "Instalar con Homebrew", + "app-store": "Ver en la App Store", + "alfred-gallery": "Instalar desde la Alfred Gallery", + "page": "Obtener {name}" + }, + "source": "Código fuente", + "details": { + "title": "Detalles", + "publisher": "Desarrollador", + "category": "Categoría", + "host": "Funciona con", + "hostVersion": "{host} {version} o posterior", + "apps": "App de TablePro", + "appVersion": "{app} {version} o posterior", + "license": "Licencia", + "added": "Añadida", + "verified": "Última comprobación" + }, + "about": "Acerca de", + "screenshots": "Capturas de pantalla", + "uses": { + "title": "Qué usa", + "surfaces": "Funciones de TablePro", + "reads": "Lee", + "writes": "Modifica", + "none": "Nada", + "data": { + "connections": "Lista de conexiones", + "schema": "Esquemas", + "data": "Datos de tablas" + }, + "surface": { + "url-scheme": { + "label": "Enlaces tablepro://", + "docs": "/developers/url-scheme" + }, + "mcp": { + "label": "Servidor MCP", + "docs": "/features/mcp" + }, + "pairing": { + "label": "Emparejamiento", + "docs": "/developers/pairing" + }, + "applescript": { + "label": "AppleScript", + "docs": "/developers/applescript" + }, + "database-url": { + "label": "URL de bases de datos", + "docs": "/connections/urls" + }, + "cli": { + "label": "Comando tablepro", + "docs": "/integrations/terminal" + }, + "shortcuts": { + "label": "Acciones de Atajos", + "docs": "/integrations/ios-shortcuts" + } + }, + "safeMode": "Baja el nivel de Safe Mode", + "safeModeLink": "Niveles de Safe Mode", + "internet": "Envía datos por internet", + "account": "Requiere una cuenta en otro servicio", + "privacy": "Política de privacidad", + "payment": { + "optional": "Gratis, con opciones de pago", + "paid": "De pago" + } + }, + "support": { + "title": "Ayuda", + "report": "Informar de un problema con esta ficha", + "help": "Obtener ayuda de {publisher}", + "docs": "Documentación" + }, + "disclaimer": "{name} lo desarrolla {publisher}, no TablePro. TablePro lo incluye en el directorio, pero no ofrece soporte para él.", + "archived": { + "badge": "Archivada", + "since": "Archivada el {date}.", + "reasons": { + "unmaintained": "Ya no tiene mantenimiento.", + "broken": "Ya no funciona con TablePro.", + "source-archived": "Su repositorio de código está archivado.", + "superseded": "La sustituye {name}.", + "author-request": "Su autor pidió archivarla.", + "policy": "Ya no cumple la política del directorio." + } + } + }, + "taglines": { + "claude-code": "Conecta Claude Code a tus bases de datos mediante el servidor MCP integrado en TablePro.", + "claude-desktop": "Conecta Claude Desktop a tus bases de datos mediante el servidor MCP integrado en TablePro.", + "command-line": "Abre URL de bases de datos y archivos SQL en TablePro desde Terminal con el comando tablepro.", + "cursor": "Conecta el agente de Cursor a tus bases de datos mediante el servidor MCP integrado en TablePro.", + "shortcuts": "Añade filas a una tabla y abre una conexión desde Atajos en iPhone y iPad.", + "zed": "Conecta el agente de Zed a tus bases de datos mediante el servidor MCP integrado en TablePro." + } +} diff --git a/resources/data/content/fr/features/ai-mcp.json b/resources/data/content/fr/features/ai-mcp.json index 15452e19..4aeac9f1 100644 --- a/resources/data/content/fr/features/ai-mcp.json +++ b/resources/data/content/fr/features/ai-mcp.json @@ -91,6 +91,12 @@ "paragraphs": [ "AppleScript peut se connecter, exécuter une requête et ouvrir une table, dans les limites du niveau External Clients et du Safe Mode de la connexion. Les liens tablepro:// ouvrent une connexion, une table ou un onglet de requête ; une requête contenue dans un lien est renseignée après confirmation et ne s’exécute jamais d’elle-même. Les URL de bases de données comme postgresql:// s’ouvrent comme des connexions.", "La commande shell tablepro ouvre ces URL et ces fichiers dans l’application ; elle n’exécute pas de requêtes depuis le terminal. L’extension officielle Raycast recherche dans vos connexions, vos tables et votre historique de requêtes, et exécute des requêtes via l’application associée." + ], + "links": [ + { + "label": "Intégrations", + "href": "/integrations" + } ] }, { diff --git a/resources/data/content/fr/integrations/index.json b/resources/data/content/fr/integrations/index.json new file mode 100644 index 00000000..a1dec7bd --- /dev/null +++ b/resources/data/content/fr/integrations/index.json @@ -0,0 +1,166 @@ +{ + "seo": { + "title": "Intégrations", + "description": "Applications et outils compatibles avec TablePro : clients IA, éditeurs, automatisation et ligne de commande, créés par TablePro ou par d’autres." + }, + "header": { + "title": "Intégrations", + "lead": "Applications et outils compatibles avec TablePro.", + "build": "Créer une intégration" + }, + "filters": { + "label": "Filtrer les intégrations", + "search": "Rechercher", + "category": { + "label": "Catégorie", + "all": "Toutes les catégories" + }, + "platform": { + "label": "Application TablePro", + "all": "Toutes" + }, + "tier": { + "label": "Type", + "all": "Tous les types" + }, + "submit": "Afficher les résultats", + "count": { + "one": "{count} intégration", + "other": "{count} intégrations" + }, + "empty": "Aucune intégration ne correspond à ces filtres.", + "clear": "Effacer les filtres" + }, + "publish": { + "title": "Référencez votre intégration", + "guide": "Comment la soumettre", + "policy": "Politique de référencement" + }, + "notice": "Les noms et logos appartiennent à leurs propriétaires. Seules les intégrations officielles sont créées par TablePro.", + "labels": { + "by": "Par {publisher}", + "closedSource": "Code source fermé", + "tiers": { + "official": "Officielle", + "partner": "Partenaire", + "community": "Communauté" + }, + "categories": { + "ai-clients": "Clients IA", + "automation": "Automatisation", + "cloud-hosting": "Hébergement cloud", + "command-line": "Ligne de commande", + "editors": "Éditeurs de code", + "launchers": "Lanceurs", + "local-development": "Développement local", + "other": "Autres" + } + }, + "show": { + "seoTitle": "Intégration {name}", + "install": { + "built-in": "Configurer dans TablePro", + "ships-with-host": "Configurer avec {host}", + "raycast-store": "Installer depuis le Raycast Store", + "vscode-marketplace": "Installer depuis le Visual Studio Marketplace", + "github-release": "Télécharger depuis GitHub", + "homebrew": "Installer avec Homebrew", + "app-store": "Voir sur l’App Store", + "alfred-gallery": "Installer depuis l’Alfred Gallery", + "page": "Obtenir {name}" + }, + "source": "Code source", + "details": { + "title": "Détails", + "publisher": "Éditeur", + "category": "Catégorie", + "host": "Fonctionne avec", + "hostVersion": "{host} {version} ou version ultérieure", + "apps": "Application TablePro", + "appVersion": "{app} {version} ou version ultérieure", + "license": "Licence", + "added": "Date d’ajout", + "verified": "Dernière vérification" + }, + "about": "À propos", + "screenshots": "Captures d’écran", + "uses": { + "title": "Ce que l’intégration utilise", + "surfaces": "Fonctionnalités de TablePro", + "reads": "Lit", + "writes": "Modifie", + "none": "Rien", + "data": { + "connections": "Liste des connexions", + "schema": "Schémas", + "data": "Données des tables" + }, + "surface": { + "url-scheme": { + "label": "Liens tablepro://", + "docs": "/developers/url-scheme" + }, + "mcp": { + "label": "Serveur MCP", + "docs": "/features/mcp" + }, + "pairing": { + "label": "Appairage", + "docs": "/developers/pairing" + }, + "applescript": { + "label": "AppleScript", + "docs": "/developers/applescript" + }, + "database-url": { + "label": "URL de bases de données", + "docs": "/connections/urls" + }, + "cli": { + "label": "Commande tablepro", + "docs": "/integrations/terminal" + }, + "shortcuts": { + "label": "Actions Raccourcis", + "docs": "/integrations/ios-shortcuts" + } + }, + "safeMode": "Abaisse le niveau de Safe Mode", + "safeModeLink": "Niveaux de Safe Mode", + "internet": "Envoie des données sur Internet", + "account": "Nécessite un compte auprès d’un autre service", + "privacy": "Politique de confidentialité", + "payment": { + "optional": "Gratuit, avec des options payantes", + "paid": "Payant" + } + }, + "support": { + "title": "Aide", + "report": "Signaler un problème avec cette fiche", + "help": "Obtenir de l’aide auprès de {publisher}", + "docs": "Documentation" + }, + "disclaimer": "{name} est développé par {publisher}, et non par TablePro. TablePro référence cette intégration mais n’en assure pas l’assistance.", + "archived": { + "badge": "Archivée", + "since": "Archivée le {date}.", + "reasons": { + "unmaintained": "Elle n’est plus maintenue.", + "broken": "Elle ne fonctionne plus avec TablePro.", + "source-archived": "Son dépôt de code source est archivé.", + "superseded": "Elle a été remplacée par {name}.", + "author-request": "Son auteur a demandé son archivage.", + "policy": "Elle ne respecte plus la politique de référencement." + } + } + }, + "taglines": { + "claude-code": "Connectez Claude Code à vos bases de données via le serveur MCP intégré à TablePro.", + "claude-desktop": "Connectez Claude Desktop à vos bases de données via le serveur MCP intégré à TablePro.", + "command-line": "Ouvrez des URL de bases de données et des fichiers SQL dans TablePro depuis le Terminal avec la commande tablepro.", + "cursor": "Connectez l’agent de Cursor à vos bases de données via le serveur MCP intégré à TablePro.", + "shortcuts": "Ajoutez des lignes à une table et ouvrez une connexion depuis l’app Raccourcis sur iPhone et iPad.", + "zed": "Connectez l’agent de Zed à vos bases de données via le serveur MCP intégré à TablePro." + } +} diff --git a/resources/data/content/id/features/ai-mcp.json b/resources/data/content/id/features/ai-mcp.json index 07c03257..2c32de39 100644 --- a/resources/data/content/id/features/ai-mcp.json +++ b/resources/data/content/id/features/ai-mcp.json @@ -91,6 +91,12 @@ "paragraphs": [ "AppleScript dapat terhubung, menjalankan kueri, dan membuka tabel, sesuai level External Clients serta Safe Mode koneksi. Tautan tablepro:// membuka koneksi, tabel, atau tab kueri; kueri dalam tautan diisikan setelah Anda mengonfirmasi dan tidak pernah berjalan sendiri. URL database seperti postgresql:// terbuka sebagai koneksi.", "Perintah shell tablepro membuka URL tersebut dan file dalam aplikasi; tidak menjalankan kueri dari terminal. Ekstensi resmi Raycast mencari koneksi, tabel, dan riwayat kueri, serta menjalankan kueri melalui aplikasi yang dipasangkan." + ], + "links": [ + { + "label": "Integrasi", + "href": "/integrations" + } ] }, { diff --git a/resources/data/content/id/integrations/index.json b/resources/data/content/id/integrations/index.json new file mode 100644 index 00000000..8af0e1aa --- /dev/null +++ b/resources/data/content/id/integrations/index.json @@ -0,0 +1,166 @@ +{ + "seo": { + "title": "Integrasi", + "description": "Aplikasi dan alat yang bekerja dengan TablePro: klien AI, editor, otomatisasi, dan baris perintah, dibuat oleh TablePro atau pihak lain." + }, + "header": { + "title": "Integrasi", + "lead": "Aplikasi dan alat yang bekerja dengan TablePro.", + "build": "Buat integrasi" + }, + "filters": { + "label": "Filter integrasi", + "search": "Cari", + "category": { + "label": "Kategori", + "all": "Semua kategori" + }, + "platform": { + "label": "Aplikasi TablePro", + "all": "Semua" + }, + "tier": { + "label": "Jenis", + "all": "Semua jenis" + }, + "submit": "Tampilkan hasil", + "count": { + "one": "{count} integrasi", + "other": "{count} integrasi" + }, + "empty": "Tidak ada integrasi yang cocok dengan filter ini.", + "clear": "Hapus filter" + }, + "publish": { + "title": "Cantumkan integrasi Anda", + "guide": "Cara mengirim", + "policy": "Kebijakan pencantuman" + }, + "notice": "Nama dan logo adalah milik pemiliknya masing-masing. Hanya integrasi Resmi yang dibuat oleh TablePro.", + "labels": { + "by": "Oleh {publisher}", + "closedSource": "Sumber tertutup", + "tiers": { + "official": "Resmi", + "partner": "Mitra", + "community": "Komunitas" + }, + "categories": { + "ai-clients": "Klien AI", + "automation": "Otomatisasi", + "cloud-hosting": "Hosting cloud", + "command-line": "Baris perintah", + "editors": "Editor", + "launchers": "Peluncur", + "local-development": "Pengembangan lokal", + "other": "Lainnya" + } + }, + "show": { + "seoTitle": "Integrasi {name}", + "install": { + "built-in": "Siapkan di TablePro", + "ships-with-host": "Siapkan dengan {host}", + "raycast-store": "Instal dari Raycast Store", + "vscode-marketplace": "Instal dari Visual Studio Marketplace", + "github-release": "Unduh dari GitHub", + "homebrew": "Instal dengan Homebrew", + "app-store": "Lihat di App Store", + "alfred-gallery": "Instal dari Alfred Gallery", + "page": "Dapatkan {name}" + }, + "source": "Kode sumber", + "details": { + "title": "Detail", + "publisher": "Penerbit", + "category": "Kategori", + "host": "Berfungsi dengan", + "hostVersion": "{host} {version} atau lebih baru", + "apps": "Aplikasi TablePro", + "appVersion": "{app} {version} atau lebih baru", + "license": "Lisensi", + "added": "Dicantumkan", + "verified": "Terakhir diperiksa" + }, + "about": "Tentang", + "screenshots": "Tangkapan layar", + "uses": { + "title": "Yang digunakan", + "surfaces": "Fitur TablePro", + "reads": "Membaca", + "writes": "Mengubah", + "none": "Tidak ada", + "data": { + "connections": "Daftar koneksi", + "schema": "Skema", + "data": "Data tabel" + }, + "surface": { + "url-scheme": { + "label": "Tautan tablepro://", + "docs": "/developers/url-scheme" + }, + "mcp": { + "label": "Server MCP", + "docs": "/features/mcp" + }, + "pairing": { + "label": "Pemasangan", + "docs": "/developers/pairing" + }, + "applescript": { + "label": "AppleScript", + "docs": "/developers/applescript" + }, + "database-url": { + "label": "URL database", + "docs": "/connections/urls" + }, + "cli": { + "label": "Perintah tablepro", + "docs": "/integrations/terminal" + }, + "shortcuts": { + "label": "Tindakan Pintasan", + "docs": "/integrations/ios-shortcuts" + } + }, + "safeMode": "Menurunkan Safe Mode", + "safeModeLink": "Level Safe Mode", + "internet": "Mengirim data lewat internet", + "account": "Memerlukan akun di layanan lain", + "privacy": "Kebijakan privasi", + "payment": { + "optional": "Gratis, dengan opsi berbayar", + "paid": "Berbayar" + } + }, + "support": { + "title": "Bantuan", + "report": "Laporkan masalah pada entri ini", + "help": "Dapatkan bantuan dari {publisher}", + "docs": "Dokumentasi" + }, + "disclaimer": "{name} dibuat oleh {publisher}, bukan oleh TablePro. TablePro mencantumkannya di sini tetapi tidak memberikan dukungan untuknya.", + "archived": { + "badge": "Diarsipkan", + "since": "Diarsipkan pada {date}.", + "reasons": { + "unmaintained": "Integrasi ini tidak lagi dikelola.", + "broken": "Integrasi ini tidak lagi berfungsi dengan TablePro.", + "source-archived": "Repositori sumbernya telah diarsipkan.", + "superseded": "Integrasi ini telah digantikan oleh {name}.", + "author-request": "Pembuatnya meminta agar integrasi ini diarsipkan.", + "policy": "Integrasi ini tidak lagi memenuhi kebijakan pencantuman." + } + } + }, + "taglines": { + "claude-code": "Hubungkan Claude Code ke database Anda melalui server MCP bawaan TablePro.", + "claude-desktop": "Hubungkan Claude Desktop ke database Anda melalui server MCP bawaan TablePro.", + "command-line": "Buka URL database dan file SQL di TablePro dari Terminal dengan perintah tablepro.", + "cursor": "Hubungkan agen Cursor ke database Anda melalui server MCP bawaan TablePro.", + "shortcuts": "Tambahkan baris ke tabel dan buka koneksi dari Pintasan di iPhone dan iPad.", + "zed": "Hubungkan agen Zed ke database Anda melalui server MCP bawaan TablePro." + } +} diff --git a/resources/data/content/it/features/ai-mcp.json b/resources/data/content/it/features/ai-mcp.json index c0fd6197..0071b69b 100644 --- a/resources/data/content/it/features/ai-mcp.json +++ b/resources/data/content/it/features/ai-mcp.json @@ -91,6 +91,12 @@ "paragraphs": [ "AppleScript può connettersi, eseguire una query e aprire una tabella, rispettando i livelli External Clients e Safe Mode della connessione. I link tablepro:// aprono una connessione, una tabella o una scheda di query; una query in un link viene inserita dopo la conferma e non viene mai eseguita autonomamente. Gli URL di database come postgresql:// si aprono come connessioni.", "Il comando shell tablepro apre questi URL e file nell’app; non esegue query dal terminale. L’estensione ufficiale Raycast cerca nelle connessioni, nelle tabelle e nella cronologia ed esegue query tramite l’app abbinata." + ], + "links": [ + { + "label": "Integrazioni", + "href": "/integrations" + } ] }, { diff --git a/resources/data/content/it/integrations/index.json b/resources/data/content/it/integrations/index.json new file mode 100644 index 00000000..096ab519 --- /dev/null +++ b/resources/data/content/it/integrations/index.json @@ -0,0 +1,166 @@ +{ + "seo": { + "title": "Integrazioni", + "description": "App e strumenti che funzionano con TablePro: client IA, editor, automazione e riga di comando, sviluppati da TablePro o da terzi." + }, + "header": { + "title": "Integrazioni", + "lead": "App e strumenti che funzionano con TablePro.", + "build": "Crea un’integrazione" + }, + "filters": { + "label": "Filtra integrazioni", + "search": "Cerca", + "category": { + "label": "Categoria", + "all": "Tutte le categorie" + }, + "platform": { + "label": "App TablePro", + "all": "Qualsiasi" + }, + "tier": { + "label": "Tipo", + "all": "Tutti i tipi" + }, + "submit": "Mostra risultati", + "count": { + "one": "{count} integrazione", + "other": "{count} integrazioni" + }, + "empty": "Nessuna integrazione corrisponde a questi filtri.", + "clear": "Rimuovi filtri" + }, + "publish": { + "title": "Aggiungi la tua integrazione", + "guide": "Come inviarla", + "policy": "Criteri di inclusione" + }, + "notice": "Nomi e loghi appartengono ai rispettivi proprietari. TablePro sviluppa solo le integrazioni di tipo Ufficiale.", + "labels": { + "by": "Di {publisher}", + "closedSource": "Codice chiuso", + "tiers": { + "official": "Ufficiale", + "partner": "Partner", + "community": "Community" + }, + "categories": { + "ai-clients": "Client IA", + "automation": "Automazione", + "cloud-hosting": "Hosting cloud", + "command-line": "Riga di comando", + "editors": "Editor", + "launchers": "Launcher", + "local-development": "Sviluppo locale", + "other": "Altro" + } + }, + "show": { + "seoTitle": "Integrazione con {name}", + "install": { + "built-in": "Configura in TablePro", + "ships-with-host": "Configura in {host}", + "raycast-store": "Installa dal Raycast Store", + "vscode-marketplace": "Installa dal Visual Studio Marketplace", + "github-release": "Scarica da GitHub", + "homebrew": "Installa con Homebrew", + "app-store": "Vedi sull’App Store", + "alfred-gallery": "Installa dalla Alfred Gallery", + "page": "Ottieni {name}" + }, + "source": "Codice sorgente", + "details": { + "title": "Dettagli", + "publisher": "Sviluppatore", + "category": "Categoria", + "host": "Funziona con", + "hostVersion": "{host} {version} o successivo", + "apps": "App TablePro", + "appVersion": "{app} {version} o successivo", + "license": "Licenza", + "added": "Aggiunta il", + "verified": "Ultimo controllo" + }, + "about": "Informazioni", + "screenshots": "Schermate", + "uses": { + "title": "Cosa usa", + "surfaces": "Funzionalità di TablePro", + "reads": "Legge", + "writes": "Modifica", + "none": "Niente", + "data": { + "connections": "Elenco delle connessioni", + "schema": "Schemi", + "data": "Dati delle tabelle" + }, + "surface": { + "url-scheme": { + "label": "Link tablepro://", + "docs": "/developers/url-scheme" + }, + "mcp": { + "label": "Server MCP", + "docs": "/features/mcp" + }, + "pairing": { + "label": "Abbinamento", + "docs": "/developers/pairing" + }, + "applescript": { + "label": "AppleScript", + "docs": "/developers/applescript" + }, + "database-url": { + "label": "URL di database", + "docs": "/connections/urls" + }, + "cli": { + "label": "Comando tablepro", + "docs": "/integrations/terminal" + }, + "shortcuts": { + "label": "Azioni di Comandi Rapidi", + "docs": "/integrations/ios-shortcuts" + } + }, + "safeMode": "Abbassa il livello Safe Mode", + "safeModeLink": "Livelli di Safe Mode", + "internet": "Invia dati via internet", + "account": "Richiede un account su un altro servizio", + "privacy": "Informativa sulla privacy", + "payment": { + "optional": "Gratuita, con opzioni a pagamento", + "paid": "A pagamento" + } + }, + "support": { + "title": "Assistenza", + "report": "Segnala un problema con questa voce", + "help": "Chiedi aiuto a {publisher}", + "docs": "Documentazione" + }, + "disclaimer": "{name} è sviluppato da {publisher}, non da TablePro. TablePro lo elenca qui, ma non offre assistenza.", + "archived": { + "badge": "Archiviata", + "since": "Archiviata il {date}.", + "reasons": { + "unmaintained": "Non è più mantenuta.", + "broken": "Non funziona più con TablePro.", + "source-archived": "Il repository del codice sorgente è archiviato.", + "superseded": "È stata sostituita da {name}.", + "author-request": "L’autore ha chiesto di archiviarla.", + "policy": "Non soddisfa più i criteri di inclusione." + } + } + }, + "taglines": { + "claude-code": "Collega Claude Code ai tuoi database tramite il server MCP integrato in TablePro.", + "claude-desktop": "Collega Claude Desktop ai tuoi database tramite il server MCP integrato in TablePro.", + "command-line": "Apri URL di database e file SQL in TablePro dal Terminale con il comando tablepro.", + "cursor": "Collega l’agente di Cursor ai tuoi database tramite il server MCP integrato in TablePro.", + "shortcuts": "Aggiungi righe a una tabella e apri una connessione da Comandi Rapidi su iPhone e iPad.", + "zed": "Collega l’agente di Zed ai tuoi database tramite il server MCP integrato in TablePro." + } +} diff --git a/resources/data/content/ja/features/ai-mcp.json b/resources/data/content/ja/features/ai-mcp.json index bf1d5231..606ad19f 100644 --- a/resources/data/content/ja/features/ai-mcp.json +++ b/resources/data/content/ja/features/ai-mcp.json @@ -91,7 +91,8 @@ "paragraphs": [ "AppleScript は、接続の External Clients レベルと Safe Mode に従って接続、クエリ実行、テーブル表示を行えます。tablepro:// リンクは接続、テーブルやクエリタブを開きます。リンク内のクエリは確認後に入力され、自動実行しません。postgresql:// などのデータベース URL は接続として開きます。", "シェルコマンド tablepro はそれらの URL やファイルをアプリで開き、ターミナルからクエリは実行しません。公式 Raycast 拡張は接続、テーブルとクエリ履歴を検索し、ペアリングしたアプリ経由でクエリを実行します。" - ] + ], + "links": [{ "label": "連携", "href": "/integrations" }] }, { "id": "privacy", diff --git a/resources/data/content/ja/integrations/index.json b/resources/data/content/ja/integrations/index.json new file mode 100644 index 00000000..75ca6739 --- /dev/null +++ b/resources/data/content/ja/integrations/index.json @@ -0,0 +1,166 @@ +{ + "seo": { + "title": "連携", + "description": "AI クライアント、エディター、自動化、コマンドラインなど、TablePro と連携するアプリとツール。TablePro が開発したものと、他の開発者によるものがあります。" + }, + "header": { + "title": "連携", + "lead": "TablePro と連携するアプリとツール。", + "build": "連携を開発する" + }, + "filters": { + "label": "連携を絞り込む", + "search": "検索", + "category": { + "label": "カテゴリ", + "all": "すべてのカテゴリ" + }, + "platform": { + "label": "TablePro アプリ", + "all": "すべて" + }, + "tier": { + "label": "種類", + "all": "すべての種類" + }, + "submit": "結果を表示", + "count": { + "one": "{count} 件の連携", + "other": "{count} 件の連携" + }, + "empty": "条件に一致する連携はありません。", + "clear": "絞り込みを解除" + }, + "publish": { + "title": "連携を掲載する", + "guide": "申請方法", + "policy": "掲載ポリシー" + }, + "notice": "名称とロゴは各権利者に帰属します。TablePro が開発しているのは公式の連携だけです。", + "labels": { + "by": "提供:{publisher}", + "closedSource": "クローズドソース", + "tiers": { + "official": "公式", + "partner": "パートナー", + "community": "コミュニティ" + }, + "categories": { + "ai-clients": "AI クライアント", + "automation": "自動化", + "cloud-hosting": "クラウドホスティング", + "command-line": "コマンドライン", + "editors": "エディター", + "launchers": "ランチャー", + "local-development": "ローカル開発", + "other": "その他" + } + }, + "show": { + "seoTitle": "{name} 連携", + "install": { + "built-in": "TablePro で設定", + "ships-with-host": "{host} で設定", + "raycast-store": "Raycast Store からインストール", + "vscode-marketplace": "Visual Studio Marketplace からインストール", + "github-release": "GitHub からダウンロード", + "homebrew": "Homebrew でインストール", + "app-store": "App Store で見る", + "alfred-gallery": "Alfred Gallery からインストール", + "page": "{name} を入手" + }, + "source": "ソースコード", + "details": { + "title": "詳細", + "publisher": "提供元", + "category": "カテゴリ", + "host": "対応アプリ", + "hostVersion": "{host} {version} 以降", + "apps": "TablePro アプリ", + "appVersion": "{app} {version} 以降", + "license": "ライセンス", + "added": "掲載日", + "verified": "最終確認日" + }, + "about": "概要", + "screenshots": "スクリーンショット", + "uses": { + "title": "利用する機能とデータ", + "surfaces": "TablePro の機能", + "reads": "読み取り", + "writes": "変更", + "none": "なし", + "data": { + "connections": "接続の一覧", + "schema": "スキーマ", + "data": "テーブルのデータ" + }, + "surface": { + "url-scheme": { + "label": "tablepro:// リンク", + "docs": "/developers/url-scheme" + }, + "mcp": { + "label": "MCP サーバー", + "docs": "/features/mcp" + }, + "pairing": { + "label": "ペアリング", + "docs": "/developers/pairing" + }, + "applescript": { + "label": "AppleScript", + "docs": "/developers/applescript" + }, + "database-url": { + "label": "データベース URL", + "docs": "/connections/urls" + }, + "cli": { + "label": "tablepro コマンド", + "docs": "/integrations/terminal" + }, + "shortcuts": { + "label": "ショートカットのアクション", + "docs": "/integrations/ios-shortcuts" + } + }, + "safeMode": "Safe Mode のレベルを下げる", + "safeModeLink": "Safe Mode のレベル", + "internet": "インターネット経由でデータを送信", + "account": "他のサービスのアカウントが必要", + "privacy": "プライバシーポリシー", + "payment": { + "optional": "無料(有料オプションあり)", + "paid": "有料" + } + }, + "support": { + "title": "ヘルプ", + "report": "この掲載内容の問題を報告", + "help": "{publisher} のサポートを受ける", + "docs": "ドキュメント" + }, + "disclaimer": "{name} は TablePro ではなく {publisher} が開発しています。TablePro はここに掲載していますが、サポートはしていません。", + "archived": { + "badge": "アーカイブ済み", + "since": "{date} にアーカイブされました。", + "reasons": { + "unmaintained": "メンテナンスが終了しています。", + "broken": "TablePro で動作しなくなりました。", + "source-archived": "ソースリポジトリがアーカイブされています。", + "superseded": "{name} に置き換えられました。", + "author-request": "作者の依頼によりアーカイブされました。", + "policy": "掲載ポリシーを満たさなくなりました。" + } + } + }, + "taglines": { + "claude-code": "TablePro 内蔵の MCP サーバーを通じて、Claude Code をデータベースに接続します。", + "claude-desktop": "TablePro 内蔵の MCP サーバーを通じて、Claude Desktop をデータベースに接続します。", + "command-line": "ターミナルから tablepro コマンドで、データベース URL や SQL ファイルを TablePro で開きます。", + "cursor": "TablePro 内蔵の MCP サーバーを通じて、Cursor のエージェントをデータベースに接続します。", + "shortcuts": "iPhone と iPad のショートカットから、テーブルに行を追加したり接続を開いたりできます。", + "zed": "TablePro 内蔵の MCP サーバーを通じて、Zed のエージェントをデータベースに接続します。" + } +} diff --git a/resources/data/content/ko/features/ai-mcp.json b/resources/data/content/ko/features/ai-mcp.json index 72294885..f01a2073 100644 --- a/resources/data/content/ko/features/ai-mcp.json +++ b/resources/data/content/ko/features/ai-mcp.json @@ -91,7 +91,8 @@ "paragraphs": [ "AppleScript는 연결의 External Clients 수준과 Safe Mode 안에서 연결하고 쿼리를 실행하고 테이블을 열 수 있습니다. tablepro:// 링크는 연결, 테이블 또는 쿼리 탭을 엽니다. 링크의 쿼리는 확인 후 입력되며 자동으로 실행되지 않습니다. postgresql:// 같은 데이터베이스 URL은 연결로 열립니다.", "tablepro 셸 명령은 해당 URL과 파일을 앱에서 열며 터미널에서 쿼리를 실행하지 않습니다. 공식 Raycast 확장은 연결, 테이블 및 쿼리 기록을 검색하고 페어링된 앱을 통해 쿼리를 실행합니다." - ] + ], + "links": [{ "label": "통합", "href": "/integrations" }] }, { "id": "privacy", diff --git a/resources/data/content/ko/integrations/index.json b/resources/data/content/ko/integrations/index.json new file mode 100644 index 00000000..0cefa995 --- /dev/null +++ b/resources/data/content/ko/integrations/index.json @@ -0,0 +1,166 @@ +{ + "seo": { + "title": "통합", + "description": "AI 클라이언트, 편집기, 자동화, 명령줄 등 TablePro와 함께 작동하는 앱과 도구입니다. TablePro가 만든 것과 다른 개발자가 만든 것이 있습니다." + }, + "header": { + "title": "통합", + "lead": "TablePro와 함께 작동하는 앱과 도구입니다.", + "build": "통합 만들기" + }, + "filters": { + "label": "통합 필터", + "search": "검색", + "category": { + "label": "카테고리", + "all": "모든 카테고리" + }, + "platform": { + "label": "TablePro 앱", + "all": "전체" + }, + "tier": { + "label": "유형", + "all": "모든 유형" + }, + "submit": "결과 보기", + "count": { + "one": "통합 {count}개", + "other": "통합 {count}개" + }, + "empty": "필터에 맞는 통합이 없습니다.", + "clear": "필터 지우기" + }, + "publish": { + "title": "통합 등록하기", + "guide": "제출 방법", + "policy": "등록 정책" + }, + "notice": "이름과 로고의 권리는 각 소유자에게 있습니다. TablePro가 만드는 통합은 공식 통합뿐입니다.", + "labels": { + "by": "{publisher} 제공", + "closedSource": "비공개 소스", + "tiers": { + "official": "공식", + "partner": "파트너", + "community": "커뮤니티" + }, + "categories": { + "ai-clients": "AI 클라이언트", + "automation": "자동화", + "cloud-hosting": "클라우드 호스팅", + "command-line": "명령줄", + "editors": "편집기", + "launchers": "런처", + "local-development": "로컬 개발", + "other": "기타" + } + }, + "show": { + "seoTitle": "{name} 통합", + "install": { + "built-in": "TablePro에서 설정", + "ships-with-host": "{host}에서 설정", + "raycast-store": "Raycast Store에서 설치", + "vscode-marketplace": "Visual Studio Marketplace에서 설치", + "github-release": "GitHub에서 다운로드", + "homebrew": "Homebrew로 설치", + "app-store": "App Store에서 보기", + "alfred-gallery": "Alfred Gallery에서 설치", + "page": "{name} 받기" + }, + "source": "소스 코드", + "details": { + "title": "세부 정보", + "publisher": "게시자", + "category": "카테고리", + "host": "호환 앱", + "hostVersion": "{host} {version} 이상", + "apps": "TablePro 앱", + "appVersion": "{app} {version} 이상", + "license": "라이선스", + "added": "등록일", + "verified": "마지막 확인" + }, + "about": "소개", + "screenshots": "스크린샷", + "uses": { + "title": "사용하는 기능과 데이터", + "surfaces": "TablePro 기능", + "reads": "읽기", + "writes": "변경", + "none": "없음", + "data": { + "connections": "연결 목록", + "schema": "스키마", + "data": "테이블 데이터" + }, + "surface": { + "url-scheme": { + "label": "tablepro:// 링크", + "docs": "/developers/url-scheme" + }, + "mcp": { + "label": "MCP 서버", + "docs": "/features/mcp" + }, + "pairing": { + "label": "페어링", + "docs": "/developers/pairing" + }, + "applescript": { + "label": "AppleScript", + "docs": "/developers/applescript" + }, + "database-url": { + "label": "데이터베이스 URL", + "docs": "/connections/urls" + }, + "cli": { + "label": "tablepro 명령", + "docs": "/integrations/terminal" + }, + "shortcuts": { + "label": "단축어 동작", + "docs": "/integrations/ios-shortcuts" + } + }, + "safeMode": "Safe Mode 수준을 낮춤", + "safeModeLink": "Safe Mode 수준", + "internet": "인터넷으로 데이터 전송", + "account": "다른 서비스 계정 필요", + "privacy": "개인정보 처리방침", + "payment": { + "optional": "무료, 유료 옵션 있음", + "paid": "유료" + } + }, + "support": { + "title": "도움말", + "report": "이 등록 정보의 문제 신고", + "help": "{publisher}에 도움 요청", + "docs": "문서" + }, + "disclaimer": "{name}의 제작자는 TablePro가 아닌 {publisher}입니다. TablePro는 이 목록에 올려 두지만 지원하지는 않습니다.", + "archived": { + "badge": "보관됨", + "since": "{date}에 보관되었습니다.", + "reasons": { + "unmaintained": "더 이상 유지 관리되지 않습니다.", + "broken": "더 이상 TablePro에서 작동하지 않습니다.", + "source-archived": "소스 저장소가 보관되었습니다.", + "superseded": "다음으로 대체되었습니다: {name}.", + "author-request": "작성자의 요청으로 보관되었습니다.", + "policy": "더 이상 등록 정책을 충족하지 않습니다." + } + } + }, + "taglines": { + "claude-code": "TablePro에 내장된 MCP 서버를 통해 Claude Code를 데이터베이스에 연결합니다.", + "claude-desktop": "TablePro에 내장된 MCP 서버를 통해 Claude Desktop을 데이터베이스에 연결합니다.", + "command-line": "터미널에서 tablepro 명령으로 데이터베이스 URL과 SQL 파일을 TablePro에서 엽니다.", + "cursor": "TablePro에 내장된 MCP 서버를 통해 Cursor의 에이전트를 데이터베이스에 연결합니다.", + "shortcuts": "iPhone과 iPad의 단축어에서 테이블에 행을 추가하고 연결을 엽니다.", + "zed": "TablePro에 내장된 MCP 서버를 통해 Zed의 에이전트를 데이터베이스에 연결합니다." + } +} diff --git a/resources/data/content/pt-BR/features/ai-mcp.json b/resources/data/content/pt-BR/features/ai-mcp.json index 19d1476b..a00ded06 100644 --- a/resources/data/content/pt-BR/features/ai-mcp.json +++ b/resources/data/content/pt-BR/features/ai-mcp.json @@ -91,6 +91,12 @@ "paragraphs": [ "AppleScript pode conectar, executar consultas e abrir tabelas, respeitando o nível de External Clients e o Safe Mode da conexão. Links tablepro:// abrem uma conexão, tabela ou aba de consulta; uma consulta em um link é preenchida após sua confirmação e nunca é executada sozinha. URLs de banco como postgresql:// abrem como conexões.", "O comando de shell tablepro abre essas URLs e arquivos no app; não executa consultas pelo terminal. A extensão oficial Raycast pesquisa conexões, tabelas e histórico de consultas e executa consultas pelo app pareado." + ], + "links": [ + { + "label": "Integrações", + "href": "/integrations" + } ] }, { diff --git a/resources/data/content/pt-BR/integrations/index.json b/resources/data/content/pt-BR/integrations/index.json new file mode 100644 index 00000000..73ac31d1 --- /dev/null +++ b/resources/data/content/pt-BR/integrations/index.json @@ -0,0 +1,166 @@ +{ + "seo": { + "title": "Integrações", + "description": "Apps e ferramentas que funcionam com o TablePro: clientes de IA, editores, automação e linha de comando, do TablePro ou de terceiros." + }, + "header": { + "title": "Integrações", + "lead": "Apps e ferramentas que funcionam com o TablePro.", + "build": "Criar uma integração" + }, + "filters": { + "label": "Filtrar integrações", + "search": "Pesquisar", + "category": { + "label": "Categoria", + "all": "Todas as categorias" + }, + "platform": { + "label": "App do TablePro", + "all": "Qualquer" + }, + "tier": { + "label": "Tipo", + "all": "Todos os tipos" + }, + "submit": "Mostrar resultados", + "count": { + "one": "{count} integração", + "other": "{count} integrações" + }, + "empty": "Nenhuma integração corresponde a esses filtros.", + "clear": "Limpar filtros" + }, + "publish": { + "title": "Adicione sua integração", + "guide": "Como enviar", + "policy": "Política do diretório" + }, + "notice": "Nomes e logotipos pertencem aos seus proprietários. Só as integrações do tipo Oficial são desenvolvidas pelo TablePro.", + "labels": { + "by": "Por {publisher}", + "closedSource": "Código fechado", + "tiers": { + "official": "Oficial", + "partner": "Parceiro", + "community": "Comunidade" + }, + "categories": { + "ai-clients": "Clientes de IA", + "automation": "Automação", + "cloud-hosting": "Hospedagem em nuvem", + "command-line": "Linha de comando", + "editors": "Editores", + "launchers": "Lançadores", + "local-development": "Desenvolvimento local", + "other": "Outros" + } + }, + "show": { + "seoTitle": "Integração com {name}", + "install": { + "built-in": "Configurar no TablePro", + "ships-with-host": "Configurar no {host}", + "raycast-store": "Instalar pela Raycast Store", + "vscode-marketplace": "Instalar pelo Visual Studio Marketplace", + "github-release": "Baixar do GitHub", + "homebrew": "Instalar com o Homebrew", + "app-store": "Ver na App Store", + "alfred-gallery": "Instalar pela Alfred Gallery", + "page": "Obter {name}" + }, + "source": "Código-fonte", + "details": { + "title": "Detalhes", + "publisher": "Desenvolvedor", + "category": "Categoria", + "host": "Funciona com", + "hostVersion": "{host} {version} ou posterior", + "apps": "App do TablePro", + "appVersion": "{app} {version} ou posterior", + "license": "Licença", + "added": "Adicionada em", + "verified": "Última verificação" + }, + "about": "Sobre", + "screenshots": "Capturas de tela", + "uses": { + "title": "O que usa", + "surfaces": "Recursos do TablePro", + "reads": "Lê", + "writes": "Altera", + "none": "Nada", + "data": { + "connections": "Lista de conexões", + "schema": "Esquemas", + "data": "Dados das tabelas" + }, + "surface": { + "url-scheme": { + "label": "Links tablepro://", + "docs": "/developers/url-scheme" + }, + "mcp": { + "label": "Servidor MCP", + "docs": "/features/mcp" + }, + "pairing": { + "label": "Pareamento", + "docs": "/developers/pairing" + }, + "applescript": { + "label": "AppleScript", + "docs": "/developers/applescript" + }, + "database-url": { + "label": "URLs de banco de dados", + "docs": "/connections/urls" + }, + "cli": { + "label": "Comando tablepro", + "docs": "/integrations/terminal" + }, + "shortcuts": { + "label": "Ações dos Atalhos", + "docs": "/integrations/ios-shortcuts" + } + }, + "safeMode": "Reduz o nível do Safe Mode", + "safeModeLink": "Níveis do Safe Mode", + "internet": "Envia dados pela internet", + "account": "Exige uma conta em outro serviço", + "privacy": "Política de privacidade", + "payment": { + "optional": "Grátis, com opções pagas", + "paid": "Pago" + } + }, + "support": { + "title": "Ajuda", + "report": "Relatar um problema nesta página", + "help": "Obter ajuda de {publisher}", + "docs": "Documentação" + }, + "disclaimer": "{name} é desenvolvido por {publisher}, não pelo TablePro. O TablePro o lista aqui, mas não oferece suporte a ele.", + "archived": { + "badge": "Arquivada", + "since": "Arquivada em {date}.", + "reasons": { + "unmaintained": "Não recebe mais manutenção.", + "broken": "Não funciona mais com o TablePro.", + "source-archived": "O repositório do código-fonte foi arquivado.", + "superseded": "Foi substituída por {name}.", + "author-request": "O autor pediu para arquivá-la.", + "policy": "Não atende mais à política do diretório." + } + } + }, + "taglines": { + "claude-code": "Conecte o Claude Code aos seus bancos de dados pelo servidor MCP integrado ao TablePro.", + "claude-desktop": "Conecte o Claude Desktop aos seus bancos de dados pelo servidor MCP integrado ao TablePro.", + "command-line": "Abra URLs de banco de dados e arquivos SQL no TablePro pelo Terminal com o comando tablepro.", + "cursor": "Conecte o agente do Cursor aos seus bancos de dados pelo servidor MCP integrado ao TablePro.", + "shortcuts": "Adicione linhas a uma tabela e abra uma conexão pelos Atalhos no iPhone e iPad.", + "zed": "Conecte o agente do Zed aos seus bancos de dados pelo servidor MCP integrado ao TablePro." + } +} diff --git a/resources/data/content/vi/features/ai-mcp.json b/resources/data/content/vi/features/ai-mcp.json index 7f3991d0..72380f93 100644 --- a/resources/data/content/vi/features/ai-mcp.json +++ b/resources/data/content/vi/features/ai-mcp.json @@ -91,6 +91,12 @@ "paragraphs": [ "AppleScript có thể kết nối, chạy query và mở table, trong phạm vi mức External Clients và Safe Mode của connection. Liên kết tablepro:// mở một connection, một table hoặc một tab query; query trong liên kết được điền sẵn sau khi bạn xác nhận và không bao giờ tự chạy. URL cơ sở dữ liệu như postgresql:// mở thành connection.", "Lệnh shell tablepro mở các URL và file đó trong ứng dụng; lệnh này không chạy query từ terminal. Extension Raycast chính thức tìm connection, table và lịch sử query của bạn, và chạy query qua ứng dụng đã ghép nối." + ], + "links": [ + { + "label": "Tích hợp", + "href": "/integrations" + } ] }, { diff --git a/resources/data/content/vi/integrations/index.json b/resources/data/content/vi/integrations/index.json new file mode 100644 index 00000000..dbd8ff74 --- /dev/null +++ b/resources/data/content/vi/integrations/index.json @@ -0,0 +1,166 @@ +{ + "seo": { + "title": "Tích hợp", + "description": "Ứng dụng và công cụ dùng được với TablePro: AI client, editor, tự động hóa và dòng lệnh, do TablePro hoặc bên khác phát triển." + }, + "header": { + "title": "Tích hợp", + "lead": "Ứng dụng và công cụ dùng được với TablePro.", + "build": "Tự xây dựng tích hợp" + }, + "filters": { + "label": "Lọc tích hợp", + "search": "Tìm kiếm", + "category": { + "label": "Danh mục", + "all": "Mọi danh mục" + }, + "platform": { + "label": "Ứng dụng TablePro", + "all": "Tất cả" + }, + "tier": { + "label": "Loại", + "all": "Mọi loại" + }, + "submit": "Xem kết quả", + "count": { + "one": "{count} tích hợp", + "other": "{count} tích hợp" + }, + "empty": "Không có tích hợp nào khớp với bộ lọc này.", + "clear": "Xóa bộ lọc" + }, + "publish": { + "title": "Đăng tích hợp của bạn", + "guide": "Cách gửi tích hợp", + "policy": "Chính sách đăng tích hợp" + }, + "notice": "Tên và logo thuộc về chủ sở hữu của chúng. Chỉ tích hợp Chính thức do TablePro phát triển.", + "labels": { + "by": "Do {publisher} phát triển", + "closedSource": "Mã nguồn đóng", + "tiers": { + "official": "Chính thức", + "partner": "Đối tác", + "community": "Cộng đồng" + }, + "categories": { + "ai-clients": "AI client", + "automation": "Tự động hóa", + "cloud-hosting": "Hosting trên cloud", + "command-line": "Dòng lệnh", + "editors": "Editor", + "launchers": "Trình khởi chạy", + "local-development": "Phát triển cục bộ", + "other": "Khác" + } + }, + "show": { + "seoTitle": "Tích hợp {name}", + "install": { + "built-in": "Thiết lập trong TablePro", + "ships-with-host": "Thiết lập bằng {host}", + "raycast-store": "Cài từ Raycast Store", + "vscode-marketplace": "Cài từ Visual Studio Marketplace", + "github-release": "Tải về từ GitHub", + "homebrew": "Cài bằng Homebrew", + "app-store": "Xem trên App Store", + "alfred-gallery": "Cài từ Alfred Gallery", + "page": "Tải {name}" + }, + "source": "Mã nguồn", + "details": { + "title": "Chi tiết", + "publisher": "Nhà phát hành", + "category": "Danh mục", + "host": "Dùng với", + "hostVersion": "{host} {version} trở lên", + "apps": "Ứng dụng TablePro", + "appVersion": "{app} {version} trở lên", + "license": "License", + "added": "Ngày đăng", + "verified": "Kiểm tra lần cuối" + }, + "about": "Giới thiệu", + "screenshots": "Ảnh chụp màn hình", + "uses": { + "title": "Tích hợp dùng gì", + "surfaces": "Tính năng TablePro", + "reads": "Đọc", + "writes": "Thay đổi", + "none": "Không có", + "data": { + "connections": "Danh sách connection", + "schema": "Schema", + "data": "Dữ liệu table" + }, + "surface": { + "url-scheme": { + "label": "Liên kết tablepro://", + "docs": "/developers/url-scheme" + }, + "mcp": { + "label": "MCP server", + "docs": "/features/mcp" + }, + "pairing": { + "label": "Ghép nối", + "docs": "/developers/pairing" + }, + "applescript": { + "label": "AppleScript", + "docs": "/developers/applescript" + }, + "database-url": { + "label": "URL cơ sở dữ liệu", + "docs": "/connections/urls" + }, + "cli": { + "label": "Lệnh tablepro", + "docs": "/integrations/terminal" + }, + "shortcuts": { + "label": "Tác vụ Phím tắt", + "docs": "/integrations/ios-shortcuts" + } + }, + "safeMode": "Hạ mức Safe Mode", + "safeModeLink": "Các mức Safe Mode", + "internet": "Gửi dữ liệu qua internet", + "account": "Cần tài khoản ở một dịch vụ khác", + "privacy": "Chính sách quyền riêng tư", + "payment": { + "optional": "Miễn phí, có tùy chọn trả phí", + "paid": "Trả phí" + } + }, + "support": { + "title": "Trợ giúp", + "report": "Báo vấn đề với mục này", + "help": "Nhờ {publisher} hỗ trợ", + "docs": "Tài liệu" + }, + "disclaimer": "{name} do {publisher} phát triển, không phải TablePro. TablePro liệt kê tích hợp này ở đây nhưng không hỗ trợ nó.", + "archived": { + "badge": "Đã lưu trữ", + "since": "Đã lưu trữ ngày {date}.", + "reasons": { + "unmaintained": "Tích hợp này không còn được duy trì.", + "broken": "Tích hợp này không còn hoạt động với TablePro.", + "source-archived": "Repository mã nguồn của tích hợp này đã được lưu trữ.", + "superseded": "Tích hợp này đã được thay bằng {name}.", + "author-request": "Tác giả yêu cầu lưu trữ tích hợp này.", + "policy": "Tích hợp này không còn đáp ứng chính sách đăng tích hợp." + } + } + }, + "taglines": { + "claude-code": "Kết nối Claude Code với cơ sở dữ liệu của bạn qua MCP server tích hợp sẵn trong TablePro.", + "claude-desktop": "Kết nối Claude Desktop với cơ sở dữ liệu của bạn qua MCP server tích hợp sẵn trong TablePro.", + "command-line": "Mở URL cơ sở dữ liệu và file SQL trong TablePro từ Terminal bằng lệnh tablepro.", + "cursor": "Kết nối agent của Cursor với cơ sở dữ liệu của bạn qua MCP server tích hợp sẵn trong TablePro.", + "shortcuts": "Thêm dòng vào table và mở connection từ Phím tắt trên iPhone và iPad.", + "zed": "Kết nối agent của Zed với cơ sở dữ liệu của bạn qua MCP server tích hợp sẵn trong TablePro." + } +} diff --git a/resources/data/content/zh-Hans/features/ai-mcp.json b/resources/data/content/zh-Hans/features/ai-mcp.json index e4742df9..ef992094 100644 --- a/resources/data/content/zh-Hans/features/ai-mcp.json +++ b/resources/data/content/zh-Hans/features/ai-mcp.json @@ -91,7 +91,8 @@ "paragraphs": [ "AppleScript 可在连接的 External Clients 级别和 Safe Mode 限制内连接、运行查询和打开表。tablepro:// 链接打开连接、表或查询标签页;链接中的查询在确认后填入,从不自行运行。postgresql:// 等数据库 URL 以连接形式打开。", "tablepro shell 命令在应用中打开这些 URL 和文件,不从终端运行查询。官方 Raycast 扩展搜索连接、表和查询历史,并通过已配对应用运行查询。" - ] + ], + "links": [{ "label": "集成", "href": "/integrations" }] }, { "id": "privacy", diff --git a/resources/data/content/zh-Hans/integrations/index.json b/resources/data/content/zh-Hans/integrations/index.json new file mode 100644 index 00000000..cb6f609b --- /dev/null +++ b/resources/data/content/zh-Hans/integrations/index.json @@ -0,0 +1,166 @@ +{ + "seo": { + "title": "集成", + "description": "与 TablePro 配合使用的应用和工具:AI 客户端、编辑器、自动化和命令行,由 TablePro 或其他开发者构建。" + }, + "header": { + "title": "集成", + "lead": "与 TablePro 配合使用的应用和工具。", + "build": "构建集成" + }, + "filters": { + "label": "筛选集成", + "search": "搜索", + "category": { + "label": "类别", + "all": "所有类别" + }, + "platform": { + "label": "TablePro 应用", + "all": "全部" + }, + "tier": { + "label": "类型", + "all": "所有类型" + }, + "submit": "显示结果", + "count": { + "one": "{count} 个集成", + "other": "{count} 个集成" + }, + "empty": "没有符合这些筛选条件的集成。", + "clear": "清除筛选" + }, + "publish": { + "title": "提交您的集成", + "guide": "提交方法", + "policy": "收录政策" + }, + "notice": "名称和标志归其所有者所有。只有官方集成由 TablePro 构建。", + "labels": { + "by": "由 {publisher} 提供", + "closedSource": "闭源", + "tiers": { + "official": "官方", + "partner": "合作伙伴", + "community": "社区" + }, + "categories": { + "ai-clients": "AI 客户端", + "automation": "自动化", + "cloud-hosting": "云托管", + "command-line": "命令行", + "editors": "编辑器", + "launchers": "启动器", + "local-development": "本地开发", + "other": "其他" + } + }, + "show": { + "seoTitle": "{name} 集成", + "install": { + "built-in": "在 TablePro 中设置", + "ships-with-host": "在 {host} 中设置", + "raycast-store": "从 Raycast Store 安装", + "vscode-marketplace": "从 Visual Studio Marketplace 安装", + "github-release": "从 GitHub 下载", + "homebrew": "使用 Homebrew 安装", + "app-store": "在 App Store 中查看", + "alfred-gallery": "从 Alfred Gallery 安装", + "page": "获取 {name}" + }, + "source": "源代码", + "details": { + "title": "详细信息", + "publisher": "发布者", + "category": "类别", + "host": "适用于", + "hostVersion": "{host} {version} 或更新版本", + "apps": "TablePro 应用", + "appVersion": "{app} {version} 或更新版本", + "license": "许可证", + "added": "收录日期", + "verified": "最后检查" + }, + "about": "简介", + "screenshots": "截图", + "uses": { + "title": "使用的功能和数据", + "surfaces": "TablePro 功能", + "reads": "读取", + "writes": "更改", + "none": "无", + "data": { + "connections": "连接列表", + "schema": "结构", + "data": "表数据" + }, + "surface": { + "url-scheme": { + "label": "tablepro:// 链接", + "docs": "/developers/url-scheme" + }, + "mcp": { + "label": "MCP 服务器", + "docs": "/features/mcp" + }, + "pairing": { + "label": "配对", + "docs": "/developers/pairing" + }, + "applescript": { + "label": "AppleScript", + "docs": "/developers/applescript" + }, + "database-url": { + "label": "数据库 URL", + "docs": "/connections/urls" + }, + "cli": { + "label": "tablepro 命令", + "docs": "/integrations/terminal" + }, + "shortcuts": { + "label": "快捷指令操作", + "docs": "/integrations/ios-shortcuts" + } + }, + "safeMode": "降低 Safe Mode 级别", + "safeModeLink": "Safe Mode 级别", + "internet": "通过互联网发送数据", + "account": "需要其他服务的账户", + "privacy": "隐私政策", + "payment": { + "optional": "免费,提供付费选项", + "paid": "付费" + } + }, + "support": { + "title": "帮助", + "report": "报告此条目的问题", + "help": "向 {publisher} 获取帮助", + "docs": "文档" + }, + "disclaimer": "{name} 由 {publisher} 构建,而非 TablePro。TablePro 在此收录它,但不提供支持。", + "archived": { + "badge": "已归档", + "since": "归档于 {date}。", + "reasons": { + "unmaintained": "已不再维护。", + "broken": "已无法与 TablePro 配合使用。", + "source-archived": "其源代码仓库已归档。", + "superseded": "已被 {name} 取代。", + "author-request": "应作者要求归档。", + "policy": "已不再符合收录政策。" + } + } + }, + "taglines": { + "claude-code": "通过 TablePro 内置的 MCP 服务器,将 Claude Code 连接到您的数据库。", + "claude-desktop": "通过 TablePro 内置的 MCP 服务器,将 Claude Desktop 连接到您的数据库。", + "command-line": "在终端中使用 tablepro 命令,用 TablePro 打开数据库 URL 和 SQL 文件。", + "cursor": "通过 TablePro 内置的 MCP 服务器,将 Cursor 的智能体连接到您的数据库。", + "shortcuts": "在 iPhone 和 iPad 上通过快捷指令向表添加行、打开连接。", + "zed": "通过 TablePro 内置的 MCP 服务器,将 Zed 的智能体连接到您的数据库。" + } +} diff --git a/resources/data/content/zh-Hant/features/ai-mcp.json b/resources/data/content/zh-Hant/features/ai-mcp.json index affdd44a..fe83976b 100644 --- a/resources/data/content/zh-Hant/features/ai-mcp.json +++ b/resources/data/content/zh-Hant/features/ai-mcp.json @@ -91,7 +91,8 @@ "paragraphs": [ "AppleScript 可在連線的 External Clients 等級和 Safe Mode 限制內連線、執行查詢和開啟資料表。tablepro:// 連結開啟連線、資料表或查詢分頁;連結中的查詢在確認後填入,從不自行執行。postgresql:// 等資料庫 URL 以連線形式開啟。", "tablepro shell 指令在 App 中開啟這些 URL 和檔案,不從終端機執行查詢。官方 Raycast 擴充功能搜尋連線、資料表和查詢歷史,並透過已配對 App 執行查詢。" - ] + ], + "links": [{ "label": "整合", "href": "/integrations" }] }, { "id": "privacy", diff --git a/resources/data/content/zh-Hant/integrations/index.json b/resources/data/content/zh-Hant/integrations/index.json new file mode 100644 index 00000000..8fc6af0c --- /dev/null +++ b/resources/data/content/zh-Hant/integrations/index.json @@ -0,0 +1,166 @@ +{ + "seo": { + "title": "整合", + "description": "與 TablePro 搭配使用的 App 和工具:AI 用戶端、編輯器、自動化和命令列,由 TablePro 或其他開發者打造。" + }, + "header": { + "title": "整合", + "lead": "與 TablePro 搭配使用的 App 和工具。", + "build": "開發整合" + }, + "filters": { + "label": "篩選整合", + "search": "搜尋", + "category": { + "label": "類別", + "all": "所有類別" + }, + "platform": { + "label": "TablePro App", + "all": "全部" + }, + "tier": { + "label": "類型", + "all": "所有類型" + }, + "submit": "顯示結果", + "count": { + "one": "{count} 個整合", + "other": "{count} 個整合" + }, + "empty": "沒有符合這些篩選條件的整合。", + "clear": "清除篩選條件" + }, + "publish": { + "title": "提交您的整合", + "guide": "提交方式", + "policy": "收錄政策" + }, + "notice": "名稱和標誌歸其所有者所有。只有官方整合由 TablePro 打造。", + "labels": { + "by": "由 {publisher} 提供", + "closedSource": "封閉原始碼", + "tiers": { + "official": "官方", + "partner": "合作夥伴", + "community": "社群" + }, + "categories": { + "ai-clients": "AI 用戶端", + "automation": "自動化", + "cloud-hosting": "雲端代管", + "command-line": "命令列", + "editors": "編輯器", + "launchers": "啟動器", + "local-development": "本機開發", + "other": "其他" + } + }, + "show": { + "seoTitle": "{name} 整合", + "install": { + "built-in": "在 TablePro 中設定", + "ships-with-host": "在 {host} 中設定", + "raycast-store": "從 Raycast Store 安裝", + "vscode-marketplace": "從 Visual Studio Marketplace 安裝", + "github-release": "從 GitHub 下載", + "homebrew": "使用 Homebrew 安裝", + "app-store": "在 App Store 查看", + "alfred-gallery": "從 Alfred Gallery 安裝", + "page": "取得 {name}" + }, + "source": "原始碼", + "details": { + "title": "詳細資訊", + "publisher": "發佈者", + "category": "類別", + "host": "適用於", + "hostVersion": "{host} {version} 或更新版本", + "apps": "TablePro App", + "appVersion": "{app} {version} 或更新版本", + "license": "授權", + "added": "收錄日期", + "verified": "最後檢查" + }, + "about": "簡介", + "screenshots": "螢幕截圖", + "uses": { + "title": "使用的功能與資料", + "surfaces": "TablePro 功能", + "reads": "讀取", + "writes": "變更", + "none": "無", + "data": { + "connections": "連線清單", + "schema": "綱要", + "data": "資料表資料" + }, + "surface": { + "url-scheme": { + "label": "tablepro:// 連結", + "docs": "/developers/url-scheme" + }, + "mcp": { + "label": "MCP 伺服器", + "docs": "/features/mcp" + }, + "pairing": { + "label": "配對", + "docs": "/developers/pairing" + }, + "applescript": { + "label": "AppleScript", + "docs": "/developers/applescript" + }, + "database-url": { + "label": "資料庫 URL", + "docs": "/connections/urls" + }, + "cli": { + "label": "tablepro 指令", + "docs": "/integrations/terminal" + }, + "shortcuts": { + "label": "捷徑動作", + "docs": "/integrations/ios-shortcuts" + } + }, + "safeMode": "降低 Safe Mode 等級", + "safeModeLink": "Safe Mode 等級", + "internet": "透過網際網路傳送資料", + "account": "需要其他服務的帳戶", + "privacy": "隱私權政策", + "payment": { + "optional": "免費,提供付費選項", + "paid": "付費" + } + }, + "support": { + "title": "說明", + "report": "回報此項目的問題", + "help": "向 {publisher} 尋求協助", + "docs": "文件" + }, + "disclaimer": "{name} 由 {publisher} 打造,而非 TablePro。TablePro 在此收錄,但不提供支援。", + "archived": { + "badge": "已封存", + "since": "封存於 {date}。", + "reasons": { + "unmaintained": "已不再維護。", + "broken": "已無法與 TablePro 搭配使用。", + "source-archived": "其原始碼儲存庫已封存。", + "superseded": "已由 {name} 取代。", + "author-request": "應作者要求封存。", + "policy": "已不再符合收錄政策。" + } + } + }, + "taglines": { + "claude-code": "透過 TablePro 內建的 MCP 伺服器,將 Claude Code 連線到您的資料庫。", + "claude-desktop": "透過 TablePro 內建的 MCP 伺服器,將 Claude Desktop 連線到您的資料庫。", + "command-line": "在終端機使用 tablepro 指令,以 TablePro 開啟資料庫 URL 和 SQL 檔案。", + "cursor": "透過 TablePro 內建的 MCP 伺服器,將 Cursor 的 AI 代理連線到您的資料庫。", + "shortcuts": "在 iPhone 與 iPad 上透過捷徑將列新增至資料表,並開啟連線。", + "zed": "透過 TablePro 內建的 MCP 伺服器,將 Zed 的 AI 代理連線到您的資料庫。" + } +} diff --git a/resources/data/facts.json b/resources/data/facts.json index 5c38149b..cfc7f5d3 100644 --- a/resources/data/facts.json +++ b/resources/data/facts.json @@ -5,6 +5,7 @@ "issues": "https://github.com/TableProApp/TablePro/issues", "discussions": "https://github.com/TableProApp/TablePro/discussions", "license": "https://github.com/TableProApp/TablePro/blob/main/LICENSE", + "integrations": "https://github.com/TableProApp/integrations", "appStore": "https://apps.apple.com/app/tablepro/id6761621829", "sponsorsProgram": "https://github.com/sponsors/datlechin", "discord": "https://discord.gg/hCNmUUbnD4", diff --git a/resources/js/components/integrations/README.md b/resources/js/components/integrations/README.md new file mode 100644 index 00000000..49b3e2db --- /dev/null +++ b/resources/js/components/integrations/README.md @@ -0,0 +1,28 @@ +# Integrations pages + +`/integrations` (every locale) and `/integrations/{slug}` (English only) render from two inputs: + +| Input | File | Who writes it | +|---|---|---| +| Entries: names, summaries, links, icons, what each one uses | `resources/data/integrations.json`, `public/images/integrations/*.png` | The sync PR, copied from a TableProApp/integrations release. Never edit them here | +| Page copy, labels and taglines, one file per language | `resources/data/content/{locale}/integrations/index.json` | You | + +## The content file + +- `seo`, `header`, `filters`, `publish`, `notice`: the hub. +- `labels`: shared by the hub and the detail page (tiers, categories, "By {publisher}", "Closed source"). +- `show`: the detail page. It renders in English only today, but the keys exist in every language, so translating the page later is a content change. +- `show.uses.surface.{id}.docs`: a docs.tablepro.app path, the same in every language. +- `taglines`: one sentence per slug. The English value is the registry `summary` it was translated from. + +A value the registry adds later (a category, an install type, a surface, an archive reason) stays off the pages until this file labels it in every language. + +## Taglines + +A language other than English lists an entry only when its `taglines` has the slug and the English file's `taglines` still equals the entry's `summary`. So: + +- A new entry shows in English at once and in another language once its tagline lands there. +- A changed summary hides the entry outside English until the English tagline and its translations are updated together. +- A tagline for a slug the registry removed is ignored. + +The sync PR body lists the active slugs that are still English only. diff --git a/resources/js/components/integrations/install-action.tsx b/resources/js/components/integrations/install-action.tsx new file mode 100644 index 00000000..d65483ec --- /dev/null +++ b/resources/js/components/integrations/install-action.tsx @@ -0,0 +1,31 @@ +import Button from '@/components/ui/button'; +import { CodeBlock } from '@/components/ui/code'; +import { useI18n } from '@/i18n'; +import { relFor } from './model'; +import type { IntegrationDetail, IntegrationPageLabels } from './types'; + +interface InstallActionProps { + integration: IntegrationDetail; + labels: IntegrationPageLabels; +} + +export default function InstallAction({ integration, labels }: InstallActionProps) { + const { m, fmt } = useI18n(); + const { install } = integration; + const label = fmt(labels.install[install.type], { name: integration.name, host: integration.host?.name ?? integration.name }); + + if (install.command !== null) { + return ; + } + + if (install.url === null) { + return null; + } + + return ( + + ); +} diff --git a/resources/js/components/integrations/integration-filters.tsx b/resources/js/components/integrations/integration-filters.tsx new file mode 100644 index 00000000..0076290e --- /dev/null +++ b/resources/js/components/integrations/integration-filters.tsx @@ -0,0 +1,82 @@ +import type { FormEvent } from 'react'; +import Button from '@/components/ui/button'; +import Field, { Input } from '@/components/ui/field'; +import Select from '@/components/ui/select'; +import { useI18n } from '@/i18n'; +import type { FacetOptions } from './model'; +import type { AppPlatform, Category, IntegrationFilters as Filters, IntegrationsHubContent, Tier } from './types'; + +interface IntegrationFiltersProps { + content: IntegrationsHubContent; + filters: Filters; + options: FacetOptions; + appName: (platform: AppPlatform) => string; + onChange: (filters: Filters, urlDelay: number) => void; +} + +// A plain GET form, so the filters work before hydration and without JavaScript. +export default function IntegrationFilters({ content, filters, options, appName, onChange }: IntegrationFiltersProps) { + const { path } = useI18n(); + const copy = content.filters; + const labels = content.labels; + + const submit = (event: FormEvent) => { + event.preventDefault(); + onChange(filters, 0); + }; + + return ( +
+ + onChange({ ...filters, q: event.target.value === '' ? null : event.target.value }, 300)} + /> + + + onChange({ ...filters, platform: value === '' ? null : (value as AppPlatform) }, 0)} + options={[{ value: '', label: copy.platform.all }, ...options.platforms.map((platform) => ({ value: platform, label: appName(platform) }))]} + /> + + +