{"id":50446894,"url":"https://github.com/troychaplin/template-parts-router","last_synced_at":"2026-05-31T22:02:29.053Z","repository":{"id":354739701,"uuid":"1224620873","full_name":"troychaplin/template-parts-router","owner":"troychaplin","description":null,"archived":false,"fork":false,"pushed_at":"2026-04-29T20:55:54.000Z","size":5211,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-29T22:08:18.335Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/troychaplin.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-29T13:11:37.000Z","updated_at":"2026-04-29T20:55:58.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/troychaplin/template-parts-router","commit_stats":null,"previous_names":["troychaplin/template-parts-router"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/troychaplin/template-parts-router","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/troychaplin%2Ftemplate-parts-router","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/troychaplin%2Ftemplate-parts-router/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/troychaplin%2Ftemplate-parts-router/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/troychaplin%2Ftemplate-parts-router/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/troychaplin","download_url":"https://codeload.github.com/troychaplin/template-parts-router/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/troychaplin%2Ftemplate-parts-router/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33750476,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-31T22:02:28.130Z","updated_at":"2026-05-31T22:02:29.048Z","avatar_url":"https://github.com/troychaplin.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cimg src=\"assets/github-banner.png\" alt=\"A file splitting off into multiple files with language references\" style=\"width: 100%; height: auto;\"\u003e\n\n# Template Parts Router for WPML\n\nA drop-in WordPress block that renders the right language variant of a template part based on the active WPML language — using a file-per-language convention instead of database-stored translations.\n\nIf you build block themes, version-control them, and use Create Block Theme to keep files in sync with the editor, you've probably hit the wall where WPML's per-language template parts only exist in the database. This plugin closes that gap.\n\n## Why this exists\n\nWPML translates template parts by storing per-language copies in the `wp_posts` table. Theme authors who treat files as the source of truth — for git history, code review, CI, theme distribution, or Create Block Theme round-trips — lose that source of truth the moment a translator opens the editor. The translated copy lives only in the database, and exporting the theme collapses everything back to a single file.\n\nTemplate Parts Router takes the opposite approach. You keep one template part file per language on disk. A single router block, dropped into the canonical template part, picks the right file at render time using `wpml_current_language`. WPML stays out of template-part resolution entirely.\n\n## At a glance\n\n- **One block, no per-slot setup.** Drop `tp-router/router` into any template part.\n- **File-based.** Variants live as `parts/{slug}-{lang}.html` next to your other template parts. CBT round-trips just work.\n- **Auto-detects the surrounding slot.** Inside `parts/footer.html`, the router infers `footer` automatically. Override with `baseSlug` only if you need to.\n- **Native editor preview.** Variants render inline as real, editable blocks via `useEntityBlockEditor` — the same machinery core's `core/template-part` uses. No `ServerSideRender` opaque chunk, no broken layout cascade, no thick selection outlines.\n- **Inline editing in context.** Edit the resolved variant from inside its parent template — changes save to the variant entity, exactly like core's template-part block.\n- **Pattern variants for PHP power.** Switch a router to use theme patterns instead of template parts when you need PHP — translatable strings via `__()`, dynamic content, asset URLs, conditional logic. Same routing convention; same theme-scoped, file-based mental model.\n- **Preview-language toggle.** A control in the block inspector lets you flip which variant the editor renders. The frontend always uses the actual WPML language.\n\n## Quick start\n\n1. Install and activate the plugin.\n2. Disable WPML's translation of `wp_template_part` and `wp_template`. (One-time setup; see [docs/usage.md](docs/usage.md#required-wpml-setup) for the exact toggle path and why.)\n3. Add the router to a template part:\n\n   ```html\n   \u003c!-- parts/footer.html --\u003e\n   \u003c!-- wp:tp-router/router {\"baseSlug\":\"footer\"} /--\u003e\n   ```\n\n4. Create one file per language alongside it:\n\n   ```\n   parts/footer-en.html\n   parts/footer-fr.html\n   ```\n\nThat's it. English visitors see `footer-en.html`; French visitors see `footer-fr.html`. No database rows, no per-language fork in the Site Editor.\n\nNeed PHP in a variant — translatable strings, asset URLs, dynamic content? Switch **Variant source** to **Pattern** in the block inspector and create `patterns/footer-{lang}.php` instead. See [docs/usage.md](docs/usage.md#pattern-variants).\n\n## How it resolves\n\n```\ntemplates/index.html      ─►  \u003c!-- wp:template-part {\"slug\":\"footer\"} /--\u003e\nparts/footer.html         ─►  \u003c!-- wp:tp-router/router {\"baseSlug\":\"footer\"} /--\u003e\n                                              │\n                                              ▼\n                          apply_filters( 'wpml_current_language', 'en' )\n                                              │\n                                              ▼\n                          parts/footer-{lang}.html              (default)\n                                  — or —\n                          {theme}/footer-{lang} pattern         (variantType=\"pattern\")\n```\n\n## Documentation\n\nFull setup, conventions, the editor experience, migration from WPML-managed template parts, troubleshooting, and a walkthrough of how the resolution works under the hood: **[docs/usage.md](docs/usage.md)**.\n\n## Requirements\n\n- WordPress 6.6+\n- PHP 7.4+\n- WPML Multilingual CMS (4.6+ recommended)\n\nThe plugin declares `Requires Plugins: sitepress-multilingual-cms` in its header, so WordPress 6.5+ will refuse to activate it unless WPML is installed and active.\n\nOther multilingual plugins (Polylang, TranslatePress, etc.) are not supported in this release. The router reads the language code from the `wpml_current_language` filter; adapting it to other plugins is straightforward but out of scope here.\n\n## License\n\nGPL-2.0-or-later. See `plugin.php` header for the full license declaration.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftroychaplin%2Ftemplate-parts-router","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftroychaplin%2Ftemplate-parts-router","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftroychaplin%2Ftemplate-parts-router/lists"}