integrations: Change URL scheme for categories and /doc/ page.

We redirect existing URLs to the new URLs. We want to use
`/integrations/category/{category_slug}` for categories and
`/integrations/{integration_name}` instead of
`/integrations/doc/{integration_name}` for individual integrations page.
This commit is contained in:
Shubham Padia
2025-10-09 08:45:13 +00:00
committed by Tim Abbott
parent a9c0cc1162
commit 40dedace08
44 changed files with 198 additions and 117 deletions

View File

@@ -9,10 +9,10 @@ Zulip:
or cases where the third-party tool wants to control the formatting or cases where the third-party tool wants to control the formatting
of the messages in Zulip. of the messages in Zulip.
* Use one of our supported [integration * Use one of our supported [integration
frameworks](/integrations/meta-integration), such as the frameworks](/integrations/category/meta-integration), such as the
[Slack-compatible incoming webhook](/integrations/doc/slack_incoming), [Slack-compatible incoming webhook](/integrations/slack_incoming),
[Zapier integration](/integrations/doc/zapier), or [Zapier integration](/integrations/zapier), or
[IFTTT integration](/integrations/doc/ifttt). [IFTTT integration](/integrations/ifttt).
* Implementing an incoming webhook integration (detailed on this page), * Implementing an incoming webhook integration (detailed on this page),
where all the logic for formatting the Zulip messages lives in the where all the logic for formatting the Zulip messages lives in the
Zulip server. This is how most of [Zulip's official Zulip server. This is how most of [Zulip's official
@@ -36,7 +36,7 @@ process.
* Set up the * Set up the
[Zulip development environment](https://zulip.readthedocs.io/en/latest/development/overview.html). [Zulip development environment](https://zulip.readthedocs.io/en/latest/development/overview.html).
* Use [Zulip's JSON integration](/integrations/doc/json), * Use [Zulip's JSON integration](/integrations/json),
<https://webhook.site/>, or a similar site to capture an example <https://webhook.site/>, or a similar site to capture an example
webhook payload from the third-party service. Create a webhook payload from the third-party service. Create a
`zerver/webhooks/<mywebhook>/fixtures/` directory, and add the `zerver/webhooks/<mywebhook>/fixtures/` directory, and add the

View File

@@ -13,7 +13,7 @@ integration.
The first step in creating an incoming webhook is to examine the data that the The first step in creating an incoming webhook is to examine the data that the
service you want to integrate will be sending to Zulip. service you want to integrate will be sending to Zulip.
* Use [Zulip's JSON integration](/integrations/doc/json), * Use [Zulip's JSON integration](/integrations/json),
<https://webhook.site/>, or a similar tool to capture webhook <https://webhook.site/>, or a similar tool to capture webhook
payload(s) from the service you are integrating. Examining this data payload(s) from the service you are integrating. Examining this data
allows you to do two things: allows you to do two things:
@@ -289,7 +289,7 @@ from zerver.lib.webhooks.common import PresetUrlOption, WebhookUrlOption
Currently configured preset URL options: Currently configured preset URL options:
- **`BRANCHES`**: This preset is intended to be used for [version control - **`BRANCHES`**: This preset is intended to be used for [version control
integrations](/integrations/version-control), and adds UI for the user to integrations](/integrations/category/version-control), and adds UI for the user to
configure which branches of a project's repository will trigger Zulip configure which branches of a project's repository will trigger Zulip
notification messages. When the user specifies which branches to receive notification messages. When the user specifies which branches to receive
notifications from, the `branches` parameter will be added to the [generated notifications from, the `branches` parameter will be added to the [generated
@@ -298,14 +298,14 @@ Currently configured preset URL options:
`&branches=main%2Cdev` would be appended to the generated integration URL. `&branches=main%2Cdev` would be appended to the generated integration URL.
- **`IGNORE_PRIVATE_REPOSITORIES`**: This preset is intended to be used for - **`IGNORE_PRIVATE_REPOSITORIES`**: This preset is intended to be used for
[version control integrations](/integrations/version-control), and adds UI [version control integrations](/integrations/category/version-control), and adds UI
for the user exclude private repositories from triggering Zulip for the user exclude private repositories from triggering Zulip
notification messages. When the user selects this option, the notification messages. When the user selects this option, the
`ignore_private_repositories` boolean parameter will be added to the `ignore_private_repositories` boolean parameter will be added to the
[generated integration URL](/help/generate-integration-url). [generated integration URL](/help/generate-integration-url).
- **`MAPPING`**: This preset is intended to be used for [chat-app - **`MAPPING`**: This preset is intended to be used for [chat-app
integrations](/integrations/communication) (like Slack), and adds a integrations](/integrations/category/communication) (like Slack), and adds a
special option, **Matching Zulip channel**, to the UI for where to send special option, **Matching Zulip channel**, to the UI for where to send
Zulip notification messages. This special option maps the notification Zulip notification messages. This special option maps the notification
messages to Zulip channels that match the messages' original channel messages to Zulip channels that match the messages' original channel

View File

@@ -7,8 +7,8 @@ guide should help you find the API you need:
[already has a native integration](/integrations/). [already has a native integration](/integrations/).
* Next, check if [Zapier](https://zapier.com/apps) or * Next, check if [Zapier](https://zapier.com/apps) or
[IFTTT](https://ifttt.com/search) has an integration. [IFTTT](https://ifttt.com/search) has an integration.
[Zulip's Zapier integration](/integrations/doc/zapier) and [Zulip's Zapier integration](/integrations/zapier) and
[Zulip's IFTTT integration](/integrations/doc/ifttt) often allow [Zulip's IFTTT integration](/integrations/ifttt) often allow
integrating a new service with Zulip without writing any code. integrating a new service with Zulip without writing any code.
* If you'd like to send content into Zulip, you can * If you'd like to send content into Zulip, you can
[write a native incoming webhook integration](/api/incoming-webhooks-overview) [write a native incoming webhook integration](/api/incoming-webhooks-overview)

View File

@@ -9,7 +9,7 @@ advantage of Zulip's [topics](/help/introduction-to-topics) to organize
notification messages. Additionally, thousands of integrations are available notification messages. Additionally, thousands of integrations are available
through [Zapier](https://zapier.com/apps) and [IFTTT](https://ifttt.com/search). through [Zapier](https://zapier.com/apps) and [IFTTT](https://ifttt.com/search).
You can also [connect any webhook designed to work with You can also [connect any webhook designed to work with
Slack](/integrations/doc/slack_incoming) to Zulip. Slack](/integrations/slack_incoming) to Zulip.
If you don't find an integration you need, you can: If you don't find an integration you need, you can:
@@ -46,8 +46,8 @@ additional integrations through [Zapier](https://zapier.com/apps) and
1. Search [Zapier](https://zapier.com/apps) or [IFTTT](https://ifttt.com/search) 1. Search [Zapier](https://zapier.com/apps) or [IFTTT](https://ifttt.com/search)
for the product you'd like to connect to Zulip. for the product you'd like to connect to Zulip.
1. Follow the integration instructions for [Zapier](/integrations/doc/zapier) or 1. Follow the integration instructions for [Zapier](/integrations/zapier) or
[IFTTT](/integrations/doc/ifttt). [IFTTT](/integrations/ifttt).
{end_tabs} {end_tabs}

View File

@@ -154,15 +154,15 @@ Here are a few common macros used to document Zulip's integrations:
``` ```
For an example rendering, see For an example rendering, see
[Zulip's Beanstalk integration](https://zulip.com/integrations/doc/beanstalk). [Zulip's Beanstalk integration](https://zulip.com/integrations/beanstalk).
[github-integration]: https://zulip.com/integrations/doc/github [github-integration]: https://zulip.com/integrations/github
[zendesk]: https://zulip.com/integrations/doc/zendesk [zendesk]: https://zulip.com/integrations/zendesk
[matrix]: https://zulip.com/integrations/doc/matrix#configure-the-bridge [matrix]: https://zulip.com/integrations/matrix#configure-the-bridge
[codebase]: https://zulip.com/integrations/doc/codebase [codebase]: https://zulip.com/integrations/codebase
[beanstalk]: https://zulip.com/integrations/doc/beanstalk [beanstalk]: https://zulip.com/integrations/beanstalk
[front]: https://zulip.com/integrations/doc/front [front]: https://zulip.com/integrations/front
[gitlab]: https://zulip.com/integrations/doc/gitlab [gitlab]: https://zulip.com/integrations/gitlab
[integrations-file]: https://github.com/zulip/zulip/blob/main/zerver/lib/integrations.py [integrations-file]: https://github.com/zulip/zulip/blob/main/zerver/lib/integrations.py
## Writing guidelines ## Writing guidelines
@@ -170,7 +170,7 @@ Here are a few common macros used to document Zulip's integrations:
For the vast majority of integrations, you should just copy the docs for a For the vast majority of integrations, you should just copy the docs for a
similar integration and edit it. [Basecamp][basecamp] is a good one to copy. similar integration and edit it. [Basecamp][basecamp] is a good one to copy.
[basecamp]: https://zulip.com/integrations/doc/basecamp [basecamp]: https://zulip.com/integrations/basecamp
### General writing guidelines ### General writing guidelines

View File

@@ -881,7 +881,7 @@ _Released 2024-11-22_
- Fixed emoji appearing huge when viewing email notifications in - Fixed emoji appearing huge when viewing email notifications in
Microsoft Outlook. Microsoft Outlook.
- Fixed the [slack-compatible incoming - Fixed the [slack-compatible incoming
webhook](https://zulip.com/integrations/doc/slack_incoming) to webhook](https://zulip.com/integrations/slack_incoming) to
return success/failure HTTP responses in the correct format. return success/failure HTTP responses in the correct format.
- Fixed several bugs with the data import tools, primarily around - Fixed several bugs with the data import tools, primarily around
thumbnailing of images and input validation. thumbnailing of images and input validation.
@@ -1269,7 +1269,7 @@ _Released 2024-05-09_
- The default' topic visibility icon is no longer displayed in the inbox view, - The default' topic visibility icon is no longer displayed in the inbox view,
for a cleaner look. for a cleaner look.
- Fixed confusing wording in the [Alertmanager - Fixed confusing wording in the [Alertmanager
integration](https://zulip.com/integrations/doc/alertmanager). integration](https://zulip.com/integrations/alertmanager).
- Started allowing DMs to bots and to oneself, regardless if [DMs are in general - Started allowing DMs to bots and to oneself, regardless if [DMs are in general
restricted](https://zulip.com/help/restrict-direct-messages). restricted](https://zulip.com/help/restrict-direct-messages).
- Notices indicating that “push notifications are not working” are now - Notices indicating that “push notifications are not working” are now
@@ -1659,7 +1659,7 @@ _Released 2023-08-25_
reverse order. reverse order.
- Upgraded Python requirements. - Upgraded Python requirements.
- Updated puppet dependencies. - Updated puppet dependencies.
- Improved the [Sentry integration](https://zulip.com/integrations/doc/sentry), - Improved the [Sentry integration](https://zulip.com/integrations/sentry),
including making the “Test plugin” button in Sentry work properly. including making the “Test plugin” button in Sentry work properly.
- Reduced memory usage by replacing a custom error reporting handler with the - Reduced memory usage by replacing a custom error reporting handler with the
default Django implementation. This will result in a slight change in the default Django implementation. This will result in a slight change in the
@@ -1684,7 +1684,7 @@ _Released 2023-08-25_
[host multiple Zulip](../production/multiple-organizations.md) [host multiple Zulip](../production/multiple-organizations.md)
organizations on one server. organizations on one server.
- Fixed missing images in documentation for the - Fixed missing images in documentation for the
[“XKCD” bot](https://zulip.com/integrations/doc/xkcd). [“XKCD” bot](https://zulip.com/integrations/xkcd).
- Fixed “Back to login page” button alignment in the desktop app. - Fixed “Back to login page” button alignment in the desktop app.
- Added a reference to - Added a reference to
[PostgreSQL upgrades](../production/upgrade.md#upgrading-postgresql) [PostgreSQL upgrades](../production/upgrade.md#upgrading-postgresql)

View File

@@ -9,7 +9,7 @@ because it enables:
into Zulip. into Zulip.
- Integrating third-party services that can send email notifications - Integrating third-party services that can send email notifications
into Zulip. See the [integration into Zulip. See the [integration
documentation](https://zulip.com/integrations/doc/email) for documentation](https://zulip.com/integrations/email) for
details. details.
Once this integration is configured, each channel will have a special Once this integration is configured, each channel will have a special

View File

@@ -5,10 +5,10 @@ call](https://zulip.com/help/start-a-call) with the click of a button, using the
call provider of your choice. The call providers call provider of your choice. The call providers
supported by Zulip are: supported by Zulip are:
- [Jitsi Meet](https://zulip.com/integrations/doc/jitsi), a fully-encrypted, - [Jitsi Meet](https://zulip.com/integrations/jitsi), a fully-encrypted,
100% open source video conferencing solution. 100% open source video conferencing solution.
- [Zoom](https://zulip.com/integrations/doc/zoom) - [Zoom](https://zulip.com/integrations/zoom)
- [BigBlueButton](https://zulip.com/integrations/doc/big-blue-button) - [BigBlueButton](https://zulip.com/integrations/big-blue-button)
By default, Zulip uses the [cloud version of Jitsi Meet](https://meet.jit.si/) By default, Zulip uses the [cloud version of Jitsi Meet](https://meet.jit.si/)
as its call provider. This page documents the configurations required to support as its call provider. This page documents the configurations required to support

View File

@@ -6,7 +6,7 @@ guide to [sending outgoing email](../production/email.md). If you're trying to
configure an email integration to receive incoming email (e.g., so that users configure an email integration to receive incoming email (e.g., so that users
can reply to message notification emails via email), you might be interested in can reply to message notification emails via email), you might be interested in
our instructions for our instructions for
[setting up an email integration](https://zulip.com/integrations/doc/email). [setting up an email integration](https://zulip.com/integrations/email).
On to the documentation. Zulip's email system is fairly straightforward, On to the documentation. Zulip's email system is fairly straightforward,
with only a few things you need to know to get started. with only a few things you need to know to get started.

View File

@@ -16,9 +16,9 @@ source video conferencing solution. Organization administrators can also
change the organization's call provider. The call providers change the organization's call provider. The call providers
supported by Zulip are: supported by Zulip are:
* [Jitsi Meet](/integrations/doc/jitsi) * [Jitsi Meet](/integrations/jitsi)
* [Zoom integration](/integrations/doc/zoom) * [Zoom integration](/integrations/zoom)
* [BigBlueButton integration](/integrations/doc/big-blue-button) * [BigBlueButton integration](/integrations/big-blue-button)
<ZulipTip> <ZulipTip>
You can disable the video and voice call buttons for your organization You can disable the video and voice call buttons for your organization
@@ -55,6 +55,6 @@ instance of Jitsi Meet.
## Related articles ## Related articles
* [Start a call](/help/start-a-call) * [Start a call](/help/start-a-call)
* [Jitsi Meet integration](/integrations/doc/jitsi) * [Jitsi Meet integration](/integrations/jitsi)
* [Zoom integration](/integrations/doc/zoom) * [Zoom integration](/integrations/zoom)
* [BigBlueButton integration](/integrations/doc/big-blue-button) * [BigBlueButton integration](/integrations/big-blue-button)

View File

@@ -14,8 +14,8 @@ import UserCogIcon from "~icons/zulip-icon/user-cog";
Many [Zulip integrations](/integrations/) are webhooks. An incoming webhook Many [Zulip integrations](/integrations/) are webhooks. An incoming webhook
integration allows a third-party service, such as an [issue integration allows a third-party service, such as an [issue
tracker](/integrations/doc/github) or an [alerting tracker](/integrations/github) or an [alerting
tool](/integrations/doc/pagerduty), to post updates to Zulip. To configure tool](/integrations/pagerduty), to post updates to Zulip. To configure
where these updates will be posted, you need to generate a special Zulip where these updates will be posted, you need to generate a special Zulip
integration URL. integration URL.

View File

@@ -197,5 +197,5 @@ moving from Slack](/help/moving-from-slack).
* [Choosing between Zulip Cloud and self-hosting](/help/zulip-cloud-or-self-hosting) * [Choosing between Zulip Cloud and self-hosting](/help/zulip-cloud-or-self-hosting)
* [Moving from Slack](/help/moving-from-slack) * [Moving from Slack](/help/moving-from-slack)
* [Slack-compatible incoming webhook](/integrations/doc/slack_incoming) * [Slack-compatible incoming webhook](/integrations/slack_incoming)
* [Getting started with Zulip](/help/getting-started-with-zulip) * [Getting started with Zulip](/help/getting-started-with-zulip)

View File

@@ -15,7 +15,7 @@ advantage of Zulip's [topics](/help/introduction-to-topics) to organize
notification messages. Additionally, thousands of integrations are available notification messages. Additionally, thousands of integrations are available
through [Zapier](https://zapier.com/apps) and [IFTTT](https://ifttt.com/search). through [Zapier](https://zapier.com/apps) and [IFTTT](https://ifttt.com/search).
You can also [connect any webhook designed to work with You can also [connect any webhook designed to work with
Slack](/integrations/doc/slack_incoming) to Zulip. Slack](/integrations/slack_incoming) to Zulip.
If you don't find an integration you need, you can: If you don't find an integration you need, you can:
@@ -47,8 +47,8 @@ additional integrations through [Zapier](https://zapier.com/apps) and
<Steps> <Steps>
1. Search [Zapier](https://zapier.com/apps) or [IFTTT](https://ifttt.com/search) 1. Search [Zapier](https://zapier.com/apps) or [IFTTT](https://ifttt.com/search)
for the product you'd like to connect to Zulip. for the product you'd like to connect to Zulip.
1. Follow the integration instructions for [Zapier](/integrations/doc/zapier) or 1. Follow the integration instructions for [Zapier](/integrations/zapier) or
[IFTTT](/integrations/doc/ifttt). [IFTTT](/integrations/ifttt).
</Steps> </Steps>
### Integrate via Slack-compatible webhook API ### Integrate via Slack-compatible webhook API

View File

@@ -9,7 +9,7 @@ dedicated guide to get started.
* [Moving from **Slack**](/help/moving-from-slack). You can [import your Slack * [Moving from **Slack**](/help/moving-from-slack). You can [import your Slack
workspace](/help/import-from-slack). Zulip's [Slack-compatible incoming workspace](/help/import-from-slack). Zulip's [Slack-compatible incoming
webhook](https://zulip.com/integrations/doc/slack_incoming) also makes it easy webhook](https://zulip.com/integrations/slack_incoming) also makes it easy
to migrate any integrations. to migrate any integrations.
* [Moving from **Discord**](/help/moving-from-discord). * [Moving from **Discord**](/help/moving-from-discord).
* [Moving from **Microsoft Teams**](/help/moving-from-teams). * [Moving from **Microsoft Teams**](/help/moving-from-teams).

View File

@@ -89,7 +89,7 @@ made the decision to move to Zulip.
conversation its own space, so one channel per team should be enough to get conversation its own space, so one channel per team should be enough to get
you started. you started.
1. Move your integrations using Zulip's [Slack-compatible incoming 1. Move your integrations using Zulip's [Slack-compatible incoming
webhook](/integrations/doc/slack_incoming). You can transition to webhook](/integrations/slack_incoming). You can transition to
Zulip-native [integrations](/integrations) over time. Zulip-native [integrations](/integrations) over time.
</FlattenedSteps> </FlattenedSteps>
@@ -137,7 +137,7 @@ team.
## Related articles ## Related articles
* [Import from Slack](/help/import-from-slack) * [Import from Slack](/help/import-from-slack)
* [Slack-compatible incoming webhook](/integrations/doc/slack_incoming) * [Slack-compatible incoming webhook](/integrations/slack_incoming)
* [Trying out Zulip](/help/trying-out-zulip) * [Trying out Zulip](/help/trying-out-zulip)
* [Zulip Cloud or self-hosting?](/help/zulip-cloud-or-self-hosting) * [Zulip Cloud or self-hosting?](/help/zulip-cloud-or-self-hosting)
* [Moving to Zulip](/help/moving-to-zulip) * [Moving to Zulip](/help/moving-to-zulip)

View File

@@ -5,7 +5,7 @@ title: Request an integration
Zulip comes with over 100 native integrations. Hundreds more are Zulip comes with over 100 native integrations. Hundreds more are
available through available through
[Zapier](https://zapier.com/home), [IFTTT](https://ifttt.com/), and [Zapier](https://zapier.com/home), [IFTTT](https://ifttt.com/), and
the [Slack compatible webhook](/integrations/doc/slack_incoming). the [Slack compatible webhook](/integrations/slack_incoming).
However, sometimes there is no integration for a tool you use, or an However, sometimes there is no integration for a tool you use, or an
existing integration doesn't do what you need. If that's the case for existing integration doesn't do what you need. If that's the case for

View File

@@ -290,10 +290,10 @@ user groups:
## Set up integrations ## Set up integrations
Zulip integrates directly with dozens of products, and with hundreds Zulip integrates directly with dozens of products, and with hundreds
more through [Zapier](/integrations/doc/zapier) and more through [Zapier](/integrations/zapier) and
[IFTTT](/integrations/doc/ifttt). Popular Zulip integrations include [IFTTT](/integrations/ifttt). Popular Zulip integrations include
[GitHub](/integrations/doc/github) and [GitHub](/integrations/github) and
[Twitter](/integrations/doc/twitter). The [integrations [Twitter](/integrations/twitter). The [integrations
page](/integrations/) has instructions for integrating with each page](/integrations/) has instructions for integrating with each
product. product.

View File

@@ -92,7 +92,7 @@ you can unlink it.
## Related articles ## Related articles
* [Configure call provider](/help/configure-call-provider) * [Configure call provider](/help/configure-call-provider)
* [Jitsi Meet integration](/integrations/doc/jitsi) * [Jitsi Meet integration](/integrations/jitsi)
* [Zoom integration](/integrations/doc/zoom) * [Zoom integration](/integrations/zoom)
* [BigBlueButton integration](/integrations/doc/big-blue-button) * [BigBlueButton integration](/integrations/big-blue-button)
* [Insert a link](/help/insert-a-link) * [Insert a link](/help/insert-a-link)

View File

@@ -85,7 +85,7 @@ every message is important.
the Immersive & Geospatial division, joined End Point the day before the move to the Immersive & Geospatial division, joined End Point the day before the move to
Zulip. Zulip has always been a core tool for his work at End Point, and he has Zulip. Zulip has always been a core tool for his work at End Point, and he has
built his workflows on Zulips [integrations](/integrations/). “With the built his workflows on Zulips [integrations](/integrations/). “With the
[Jenkins CI integration](/integrations/doc/jenkins), I can see when a job has [Jenkins CI integration](/integrations/jenkins), I can see when a job has
finished building and is ready to deploy,” Alejandro says. “I use this 100s of finished building and is ready to deploy,” Alejandro says. “I use this 100s of
times per week.” times per week.”

View File

@@ -50,7 +50,7 @@ prior conversations.”
Chat integrations for operational systems and alerts are a crucial part of the Chat integrations for operational systems and alerts are a crucial part of the
workflow at iDrift AS. Zulip offers a [Slack-compatible workflow at iDrift AS. Zulip offers a [Slack-compatible
interface](/integrations/doc/slack_incoming), so many integrations could be interface](/integrations/slack_incoming), so many integrations could be
simply moved over. “For a handful of integrations where Zulip didnt meet our simply moved over. “For a handful of integrations where Zulip didnt meet our
needs, it was easy to [create our own needs, it was easy to [create our own
integration](/api/integrations-overview#write-your-own-integration) or improve integration](/api/integrations-overview#write-your-own-integration) or improve

View File

@@ -566,8 +566,8 @@
</tr> </tr>
<tr> <tr>
<td class="comparison-table-feature"> <td class="comparison-table-feature">
1000s of integrations though <a href="/integrations/doc/zapier">Zapier</a> and 1000s of integrations though <a href="/integrations/zapier">Zapier</a> and
<a href="/integrations/doc/ifttt">IFTTT</a> <a href="/integrations/ifttt">IFTTT</a>
</td> </td>
<td class="comparison-value-positive cloud-cell"><i class="icon icon-check"></i></td> <td class="comparison-value-positive cloud-cell"><i class="icon icon-check"></i></td>
<td class="comparison-value-positive cloud-cell"><i class="icon icon-check"></i></td> <td class="comparison-value-positive cloud-cell"><i class="icon icon-check"></i></td>

View File

@@ -266,7 +266,7 @@
using <a href="https://zapier.com/apps/zulip/integrations">Zapier</a>. using <a href="https://zapier.com/apps/zulip/integrations">Zapier</a>.
Integrations written for Slack can post Integrations written for Slack can post
into Zulip via into Zulip via
the <a href="https://zulip.com/integrations/doc/slack_incoming">Slack the <a href="https://zulip.com/integrations/slack_incoming">Slack
compatible webhook</a>. compatible webhook</a>.
</div> </div>
</li> </li>

View File

@@ -304,9 +304,9 @@
<li> <li>
<div class="list-content"> <div class="list-content">
Zulip supports mirroring channels with Zulip supports mirroring channels with
<a href="/integrations/doc/irc">IRC</a>, <a href="/integrations/irc">IRC</a>,
<a href="/integrations/doc/slack">Slack</a>, and <a href="/integrations/slack">Slack</a>, and
<a href="/integrations/doc/matrix">Matrix</a>, and <a href="/integrations/matrix">Matrix</a>, and
you can connect to other modern chat protocols using you can connect to other modern chat protocols using
<a href="https://github.com/42wim/matterbridge">Matterbridge</a>. <a href="https://github.com/42wim/matterbridge">Matterbridge</a>.
</div> </div>
@@ -505,7 +505,7 @@
using <a href="https://zapier.com/apps/zulip/integrations">Zapier</a>. using <a href="https://zapier.com/apps/zulip/integrations">Zapier</a>.
Integrations written for Slack can post Integrations written for Slack can post
into Zulip via into Zulip via
the <a href="/integrations/doc/slack_incoming">Slack the <a href="/integrations/slack_incoming">Slack
compatible webhook</a>. compatible webhook</a>.
</div> </div>
</li> </li>

View File

@@ -148,7 +148,7 @@
<a href="https://zulip.readthedocs.io/en/stable/production/upgrade.html">upgrade</a> <a href="https://zulip.readthedocs.io/en/stable/production/upgrade.html">upgrade</a>
your self-hosted Zulip installation. Migrate your your self-hosted Zulip installation. Migrate your
<a href="/help/migrating-from-other-chat-tools">data</a> <a href="/help/migrating-from-other-chat-tools">data</a>
and <a href="/integrations/doc/slack_incoming">integrations</a> and <a href="/integrations/slack_incoming">integrations</a>
from other chat tools for a smooth transition. from other chat tools for a smooth transition.
</p> </p>
<p> <p>

View File

@@ -44,6 +44,6 @@ as your call provider instead.
### Related documentation ### Related documentation
- [How to start a call](/help/start-a-call) - [How to start a call](/help/start-a-call)
- [Jitsi Meet integration](/integrations/doc/jitsi) - [Jitsi Meet integration](/integrations/jitsi)
- [Zoom integration](/integrations/doc/zoom) - [Zoom integration](/integrations/zoom)
* [BigBlueButton server configuration](https://docs.bigbluebutton.org/administration/customize/#other-configuration-changes) * [BigBlueButton server configuration](https://docs.bigbluebutton.org/administration/customize/#other-configuration-changes)

View File

@@ -26,9 +26,9 @@
<h2 class="portico-page-subheading"> <h2 class="portico-page-subheading">
{% trans %} {% trans %}
And hundreds more through And hundreds more through
<a href="/integrations/doc/zapier">Zapier</a> <a href="/integrations/zapier">Zapier</a>
and and
<a href="/integrations/doc/ifttt">IFTTT</a>. <a href="/integrations/ifttt">IFTTT</a>.
{% endtrans %} {% endtrans %}
</h2> </h2>
</div> </div>
@@ -56,7 +56,7 @@
<h4 class="integration-category {% if selected_category_slug == 'all' %}selected{% endif %}" data-category="all">All</h4> <h4 class="integration-category {% if selected_category_slug == 'all' %}selected{% endif %}" data-category="all">All</h4>
</a> </a>
{% for category in categories_dict.keys() %} {% for category in categories_dict.keys() %}
<a href="/integrations/{{ category }}"> <a href="/integrations/category/{{ category }}">
<h4 class="integration-category {% if selected_category_slug == category %}selected{% endif %}" data-category="{{ category }}"> <h4 class="integration-category {% if selected_category_slug == category %}selected{% endif %}" data-category="{{ category }}">
{{ categories_dict[category] }} {{ categories_dict[category] }}
</h4> </h4>
@@ -82,7 +82,7 @@
<h4 data-category="all" class="integration-category {% if selected_category_slug == 'all' %}selected{% endif %}">{% trans %}All{% endtrans %}</h4> <h4 data-category="all" class="integration-category {% if selected_category_slug == 'all' %}selected{% endif %}">{% trans %}All{% endtrans %}</h4>
</a> </a>
{% for category in categories_dict.keys() %} {% for category in categories_dict.keys() %}
<a href="/integrations/{{ category }}"> <a href="/integrations/category/{{ category }}">
<h4 data-category="{{ category }}" class="integration-category {% if selected_category_slug == category %}selected{% endif %}"> <h4 data-category="{{ category }}" class="integration-category {% if selected_category_slug == category %}selected{% endif %}">
{{ categories_dict[category] }} {{ categories_dict[category] }}
</h4> </h4>
@@ -103,7 +103,7 @@
<div class="integration-lozenges"> <div class="integration-lozenges">
{% for integration in visible_integrations %} {% for integration in visible_integrations %}
<a href="/integrations/doc/{{ integration.name }}{% if selected_category_slug != 'all' %}?category={{ selected_category_slug }}{% endif %}"> <a href="/integrations/{{ integration.name }}{% if selected_category_slug != 'all' %}?category={{ selected_category_slug }}{% endif %}">
{{ integration_macros.render_integration_lozenge(integration) }} {{ integration_macros.render_integration_lozenge(integration) }}
</a> </a>
{% endfor %} {% endfor %}

View File

@@ -22,7 +22,7 @@
{% if integration_categories %} {% if integration_categories %}
<div class="categories"> <div class="categories">
{% for slug, display_name in integration_categories %} {% for slug, display_name in integration_categories %}
<a href="/integrations/{{ slug }}"> <a href="/integrations/category/{{ slug }}">
<h3 class="integration-category" data-category="{{ slug }}"> <h3 class="integration-category" data-category="{{ slug }}">
{{ display_name }} {{ display_name }}
</h3> </h3>
@@ -31,7 +31,7 @@
</div> </div>
{% endif %} {% endif %}
{{ integration_macros.render_integration_lozenge(selected_integration, is_doc_view=true) }} {{ integration_macros.render_integration_lozenge(selected_integration, is_doc_view=true) }}
<a href="/integrations/{% if return_category_slug != 'all' %}{{ return_category_slug }}{% endif %}" id="integration-list-link" class="no-underline"> <a href="/integrations/{% if return_category_slug != 'all' %}category/{{ return_category_slug }}{% endif %}" id="integration-list-link" class="no-underline">
<i class="fa fa-arrow-circle-left" aria-hidden="true"></i><span class="integrations-back-to-list-label">Back to list</span> <i class="fa fa-arrow-circle-left" aria-hidden="true"></i><span class="integrations-back-to-list-label">Back to list</span>
</a> </a>
</div> </div>

View File

@@ -5,7 +5,7 @@ It's easy to send Zulip messages from GitHub Actions! This is useful:
Instructions are available [here](https://github.com/zulip/github-actions-zulip#readme). Instructions are available [here](https://github.com/zulip/github-actions-zulip#readme).
See also the [GitHub integration](/integrations/doc/github). See also the [GitHub integration](/integrations/github).
{!congrats.md!} {!congrats.md!}

View File

@@ -51,14 +51,14 @@ result like this:
* [GitHub repository for Zulip Hubot adapter][hubot-zulip] * [GitHub repository for Zulip Hubot adapter][hubot-zulip]
* Zulip Integrations using Hubot: [Assembla](/integrations/doc/assembla) | * Zulip Integrations using Hubot: [Assembla](/integrations/assembla) |
[Bonusly](/integrations/doc/bonusly) | [Bonusly](/integrations/bonusly) |
[Chartbeat](/integrations/doc/chartbeat) | [Chartbeat](/integrations/chartbeat) |
[Dark Sky](/integrations/doc/darksky) | [Dark Sky](/integrations/darksky) |
[Instagram](/integrations/doc/instagram) | [Instagram](/integrations/instagram) |
[Google Translate](/integrations/doc/google-translate) | [Google Translate](/integrations/google-translate) |
[MailChimp](/integrations/doc/mailchimp) | [MailChimp](/integrations/mailchimp) |
[YouTube](/integrations/doc/youtube) [YouTube](/integrations/youtube)
* [Other Hubot adapters][other-adapters] * [Other Hubot adapters][other-adapters]

View File

@@ -30,5 +30,5 @@ instance of Jitsi Meet.
## Related documentation ## Related documentation
- [How to start a call](/help/start-a-call) - [How to start a call](/help/start-a-call)
- [Zoom integration](/integrations/doc/zoom) - [Zoom integration](/integrations/zoom)
- [BigBlueButton integration](/integrations/doc/big-blue-button) - [BigBlueButton integration](/integrations/big-blue-button)

View File

@@ -37,5 +37,5 @@ in order to use this integration.
## Related documentation ## Related documentation
- [How to start a call](/help/start-a-call) - [How to start a call](/help/start-a-call)
- [Jitsi Meet integration](/integrations/doc/jitsi) - [Jitsi Meet integration](/integrations/jitsi)
- [BigBlueButton integration](/integrations/doc/big-blue-button) - [BigBlueButton integration](/integrations/big-blue-button)

View File

@@ -107,7 +107,11 @@ function integration_events(): void {
// init // init
$(() => { $(() => {
const is_doc_view = window.location.pathname.startsWith("/integrations/doc/"); const path = window.location.pathname;
const is_doc_view =
path !== "/integrations/" &&
!path.startsWith("/integrations/category/") &&
path.startsWith("/integrations/");
if (!is_doc_view) { if (!is_doc_view) {
integration_events(); integration_events();

View File

@@ -28,3 +28,34 @@ LANDING_PAGE_REDIRECTS = [
] ]
DOCUMENTATION_REDIRECTS = API_DOCUMENTATION_REDIRECTS + POLICY_DOCUMENTATION_REDIRECTS DOCUMENTATION_REDIRECTS = API_DOCUMENTATION_REDIRECTS + POLICY_DOCUMENTATION_REDIRECTS
# List of category slugs at the time of changing the URL scheme to have
# `/category` be appended before the category slug. This list does not
# need to change with changing categories.
INTEGRATION_CATEGORY_SLUGS = [
"bots",
"communication",
"continuous-integration",
"customer-support",
"deployment",
"entertainment",
"financial",
"hr",
"marketing",
"meta-integration",
"misc",
"monitoring",
"productivity",
"project-management",
"version-control",
]
def get_integration_category_redirects() -> list[URLRedirect]:
return [
URLRedirect(
f"/integrations/{slug}",
f"/integrations/category/{slug}",
)
for slug in INTEGRATION_CATEGORY_SLUGS
]

View File

@@ -25546,7 +25546,7 @@ paths:
summary: Create BigBlueButton video call summary: Create BigBlueButton video call
description: | description: |
Create a video call URL for a BigBlueButton video call. Create a video call URL for a BigBlueButton video call.
Requires [BigBlueButton 2.4+](/integrations/doc/big-blue-button) Requires [BigBlueButton 2.4+](/integrations/big-blue-button)
to be configured on the Zulip server. to be configured on the Zulip server.
The acting user will be given the moderator role on the call. The acting user will be given the moderator role on the call.

View File

@@ -16,6 +16,7 @@ from zerver.context_processors import get_apps_page_url
from zerver.lib.integrations import CATEGORIES, INTEGRATIONS, META_CATEGORY from zerver.lib.integrations import CATEGORIES, INTEGRATIONS, META_CATEGORY
from zerver.lib.test_classes import ZulipTestCase from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.test_helpers import HostRequestMock from zerver.lib.test_helpers import HostRequestMock
from zerver.lib.url_redirects import INTEGRATION_CATEGORY_SLUGS
from zerver.models import Realm from zerver.models import Realm
from zerver.models.realms import get_realm from zerver.models.realms import get_realm
from zerver.views.documentation import add_api_url_context from zerver.views.documentation import add_api_url_context
@@ -136,8 +137,8 @@ class DocPageTest(ZulipTestCase):
"/errors/404/", "/errors/404/",
"/errors/5xx/", "/errors/5xx/",
"/integrations/", "/integrations/",
"/integrations/bots", "/integrations/category/bots",
"/integrations/doc/github", "/integrations/github",
"/team/", "/team/",
] ]
@@ -391,47 +392,66 @@ class DocPageTest(ZulipTestCase):
images_in_docs.add(image) images_in_docs.add(image)
result = self.client_get( result = self.client_get(
"/integrations/nonexistent_category", "/integrations/category/nonexistent_category",
follow=True, follow=True,
) )
self.assertEqual(result.status_code, 404) self.assertEqual(result.status_code, 404)
for category_name in INTEGRATION_CATEGORY_SLUGS:
legacy_category_response = self.client_get(
f"/integrations/{category_name}",
follow=False,
)
self.assertEqual(legacy_category_response.status_code, 301)
self.assertEqual(
legacy_category_response["Location"], f"/integrations/category/{category_name}"
)
legacy_doc_response = self.client_get(
"/integrations/doc/asana?category=project-management",
follow=False,
)
self.assertEqual(legacy_doc_response.status_code, 301)
self.assertEqual(
legacy_doc_response["Location"], "/integrations/asana?category=project-management"
)
result = self.client_get( result = self.client_get(
"/integrations/doc/nonexistent_integration", "/integrations/nonexistent_integration",
follow=True, follow=True,
) )
self.assertEqual(result.status_code, 404) self.assertEqual(result.status_code, 404)
result = self._test( result = self._test(
"/integrations/doc/asana?category=project-management", "/integrations/asana?category=project-management",
expected_strings=[ expected_strings=[
'<a href="/integrations/project-management" id="integration-list-link" class="no-underline">' '<a href="/integrations/category/project-management" id="integration-list-link" class="no-underline">'
], ],
) )
result = self.client_get( result = self.client_get(
"/integrations/doc/asana?category=nonexistent_category", "/integrations/asana?category=nonexistent_category",
) )
self.assert_not_in_success_response( self.assert_not_in_success_response(
[ [
'<a href="/integrations/project-management" id="integration-list-link" class="no-underline">' '<a href="/integrations/category/project-management" id="integration-list-link" class="no-underline">'
], ],
response, response,
) )
# Mis-matched category should also return back to all. # Mis-matched category should also return back to all.
result = self.client_get( result = self.client_get(
"/integrations/doc/asana?category=communication", "/integrations/asana?category=communication",
) )
self.assert_not_in_success_response( self.assert_not_in_success_response(
[ [
'<a href="/integrations/project-management" id="integration-list-link" class="no-underline">' '<a href="/integrations/category/project-management" id="integration-list-link" class="no-underline">'
], ],
response, response,
) )
for integration in INTEGRATIONS: for integration in INTEGRATIONS:
url = f"/integrations/doc/{integration}" url = f"/integrations/{integration}"
response = self._test(url, expected_strings=[]) response = self._test(url, expected_strings=[])
doc = response.content.decode("utf-8") doc = response.content.decode("utf-8")
for image in re.findall(r"/static/images/integrations/(.*)\"", doc): for image in re.findall(r"/static/images/integrations/(.*)\"", doc):
@@ -475,14 +495,14 @@ class DocPageTest(ZulipTestCase):
og_description = '<meta property="og:description" content="Zulip comes with over' og_description = '<meta property="og:description" content="Zulip comes with over'
# Test a particular integration page # Test a particular integration page
url = "/integrations/doc/github" url = "/integrations/github"
title = '<meta property="og:title" content="GitHub | Zulip integrations" />' title = '<meta property="og:title" content="GitHub | Zulip integrations" />'
description = '<meta property="og:description" content="Zulip comes with over' description = '<meta property="og:description" content="Zulip comes with over'
self._test(url, [title, description, get_canonical_url(url)]) self._test(url, [title, description, get_canonical_url(url)])
# Test category pages # Test category pages
for category in CATEGORIES: for category in CATEGORIES:
url = f"/integrations/{category}" url = f"/integrations/category/{category}"
if category in META_CATEGORY: if category in META_CATEGORY:
title = f"<title>{CATEGORIES[category]} | Zulip integrations</title>" title = f"<title>{CATEGORIES[category]} | Zulip integrations</title>"
og_title = f'<meta property="og:title" content="{CATEGORIES[category]} | Zulip integrations" />' og_title = f'<meta property="og:title" content="{CATEGORIES[category]} | Zulip integrations" />'
@@ -500,19 +520,19 @@ class DocPageTest(ZulipTestCase):
# We don't need to test all the pages for 404 # We don't need to test all the pages for 404
for integration in list(INTEGRATIONS.keys())[5]: for integration in list(INTEGRATIONS.keys())[5]:
with self.settings(ROOT_DOMAIN_LANDING_PAGE=True): with self.settings(ROOT_DOMAIN_LANDING_PAGE=True):
url = f"/en/integrations/doc/{integration}" url = f"/en/integrations/{integration}"
result = self.client_get(url, subdomain="", follow=True) result = self.client_get(url, subdomain="", follow=True)
self.assertEqual(result.status_code, 404) self.assertEqual(result.status_code, 404)
result = self.client_get(url, subdomain="zephyr", follow=True) result = self.client_get(url, subdomain="zephyr", follow=True)
self.assertEqual(result.status_code, 404) self.assertEqual(result.status_code, 404)
url = f"/en/integrations/doc/{integration}" url = f"/en/integrations/{integration}"
result = self.client_get(url, subdomain="", follow=True) result = self.client_get(url, subdomain="", follow=True)
self.assertEqual(result.status_code, 404) self.assertEqual(result.status_code, 404)
result = self.client_get(url, subdomain="zephyr", follow=True) result = self.client_get(url, subdomain="zephyr", follow=True)
self.assertEqual(result.status_code, 404) self.assertEqual(result.status_code, 404)
result = self.client_get("/integrations/doc/nonexistent_integration", follow=True) result = self.client_get("/integrations/nonexistent_integration", follow=True)
self.assertEqual(result.status_code, 404) self.assertEqual(result.status_code, 404)
def test_electron_detection(self) -> None: def test_electron_detection(self) -> None:

View File

@@ -4,7 +4,7 @@ See your Thinkst Canarytoken alerts in Zulip!
This integration works with Canarytokens from This integration works with Canarytokens from
[canarytokens.org][canarytokens], not Thinkst's paid product. See the [canarytokens.org][canarytokens], not Thinkst's paid product. See the
[Zulip Thinkst integration](/integrations/doc/thinkst) for those! [Zulip Thinkst integration](/integrations/thinkst) for those!
{start_tabs} {start_tabs}

View File

@@ -30,7 +30,7 @@ Get GitHub notifications in Zulip!
- [GitHub's webhook events documentation][github-webhook-events] - [GitHub's webhook events documentation][github-webhook-events]
- [Zulip GitHub Actions integration](/integrations/doc/github-actions) - [Zulip GitHub Actions integration](/integrations/github-actions)
{!webhooks-url-specification.md!} {!webhooks-url-specification.md!}

View File

@@ -27,7 +27,7 @@ Get GitHub Sponsors notifications in Zulip!
- [GitHub's webhook events documentation][github-webhook-events] - [GitHub's webhook events documentation][github-webhook-events]
- [Zulip GitHub integration](/integrations/doc/github). - [Zulip GitHub integration](/integrations/github).
{!webhooks-url-specification.md!} {!webhooks-url-specification.md!}

View File

@@ -345,7 +345,7 @@ class NewRelicHookTests(WebhookTestCase):
expected_message = """ expected_message = """
:danger: A New Relic [incident](https://one.newrelic.com/alerts-ai) updated :danger: A New Relic [incident](https://one.newrelic.com/alerts-ai) updated
**Warning**: Unable to use the default notification format because at least one expected field was missing from the incident payload. See [New Relic integration documentation](/integrations/doc/newrelic). **Warning**: Unable to use the default notification format because at least one expected field was missing from the incident payload. See [New Relic integration documentation](/integrations/newrelic).
**Missing fields**: `issueUrl`, `title`, `priority`, `totalIncidents`, `state`, `createdAt`, `updatedAt`, `alertPolicyNames`, `alertConditionNames` **Missing fields**: `issueUrl`, `title`, `priority`, `totalIncidents`, `state`, `createdAt`, `updatedAt`, `alertPolicyNames`, `alertConditionNames`
""".strip() """.strip()

View File

@@ -22,7 +22,7 @@ from zerver.models import UserProfile
MISSING_FIELDS_NOTIFICATION = """ MISSING_FIELDS_NOTIFICATION = """
:danger: A New Relic [incident]({url}) updated :danger: A New Relic [incident]({url}) updated
**Warning**: Unable to use the default notification format because at least one expected field was missing from the incident payload. See [New Relic integration documentation](/integrations/doc/newrelic). **Warning**: Unable to use the default notification format because at least one expected field was missing from the incident payload. See [New Relic integration documentation](/integrations/newrelic).
**Missing fields**: {formatted_missing_fields} **Missing fields**: {formatted_missing_fields}
""" """

View File

@@ -112,7 +112,7 @@ If you are looking to quickly move your Slack integrations to Zulip, check out
{!webhooks-url-specification.md!} {!webhooks-url-specification.md!}
[1]: /integrations/doc/slack_incoming [1]: /integrations/slack_incoming
[2]: /help/create-a-channel [2]: /help/create-a-channel
[3]: https://api.slack.com/apis/events-api [3]: https://api.slack.com/apis/events-api
[4]: https://api.slack.com/apps [4]: https://api.slack.com/apps

View File

@@ -27,7 +27,7 @@ integrations when migrating your organization from Slack to Zulip.
- [Moving from Slack to Zulip](/help/moving-from-slack) - [Moving from Slack to Zulip](/help/moving-from-slack)
- [Forward Slack messages into Zulip](/integrations/doc/slack) - [Forward Slack messages into Zulip](/integrations/slack)
- [Forward messages Slack <-> Zulip][2] (both directions) - [Forward messages Slack <-> Zulip][2] (both directions)

View File

@@ -4,7 +4,7 @@ Get your Thinkst Canary and Canarytoken alerts in Zulip!
This integration works with Canarytokens from Thinkst's paid product. This integration works with Canarytokens from Thinkst's paid product.
For [canarytokens.org][canarytokens], see the For [canarytokens.org][canarytokens], see the
[Canarytokens](/integrations/doc/canarytoken) integration! [Canarytokens](/integrations/canarytoken) integration!
{start_tabs} {start_tabs}

View File

@@ -17,7 +17,7 @@ from django.views.generic import RedirectView
from zerver.forms import LoggingSetPasswordForm from zerver.forms import LoggingSetPasswordForm
from zerver.lib.integrations import WEBHOOK_INTEGRATIONS from zerver.lib.integrations import WEBHOOK_INTEGRATIONS
from zerver.lib.rest import rest_path from zerver.lib.rest import rest_path
from zerver.lib.url_redirects import DOCUMENTATION_REDIRECTS from zerver.lib.url_redirects import DOCUMENTATION_REDIRECTS, get_integration_category_redirects
from zerver.tornado.views import ( from zerver.tornado.views import (
cleanup_event_queue, cleanup_event_queue,
get_events, get_events,
@@ -279,6 +279,14 @@ if settings.TWO_FACTOR_AUTHENTICATION_ENABLED: # nocoverage
from two_factor.gateways.twilio.urls import urlpatterns as tf_twilio_urls from two_factor.gateways.twilio.urls import urlpatterns as tf_twilio_urls
from two_factor.urls import urlpatterns as tf_urls from two_factor.urls import urlpatterns as tf_urls
INTEGRATION_CATEGORY_REDIRECT_PATHS = [
path(
redirect.old_url.lstrip("/"),
RedirectView.as_view(url=redirect.new_url, permanent=True, query_string=True),
)
for redirect in get_integration_category_redirects()
]
# NB: There are several other pieces of code which route requests by URL: # NB: There are several other pieces of code which route requests by URL:
# #
# - runtornado.py has its own URL list for Tornado views. See the # - runtornado.py has its own URL list for Tornado views. See the
@@ -717,9 +725,27 @@ i18n_urls = [
# Used to join a BigBlueButton video call # Used to join a BigBlueButton video call
path("calls/bigbluebutton/join", join_bigbluebutton), path("calls/bigbluebutton/join", join_bigbluebutton),
# Integrations documentation # Integrations documentation
path("integrations/doc/<str:integration_name>", integrations_doc), path(
path("integrations/", integrations_catalog, {"category_slug": "all"}), "integrations/",
path("integrations/<str:category_slug>", integrations_catalog), integrations_catalog,
{"category_slug": "all"},
name="integrations_home",
),
path(
"integrations/category/<str:category_slug>",
integrations_catalog,
name="integrations_category",
),
*INTEGRATION_CATEGORY_REDIRECT_PATHS,
path(
"integrations/doc/<str:integration_name>",
RedirectView.as_view(pattern_name="integration_doc", permanent=True, query_string=True),
),
path(
"integrations/<str:integration_name>",
integrations_doc,
name="integration_doc",
),
] ]
# Make a copy of i18n_urls so that they appear without prefix for english # Make a copy of i18n_urls so that they appear without prefix for english