-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(guides): experimental i18n routing #5187
Merged
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
32ab8ab
feat(guides): experimental i18n routing
ematipico 843a472
chore: more APIs
ematipico a17a09d
update documentation
ematipico 9771e2e
Fix link
yanthomasdev dcf643a
Update src/content/docs/en/guides/i18n-routing.mdx
ematipico 9b7ce6e
Changes after the API bash session
ematipico 5c16659
More explanation for the fallback system
ematipico bdfb901
create page and sidebar entry
sarah11918 cccb25d
Basic setup usage draft
sarah11918 d0954fe
code sample update, might be too much but let's try it for now
sarah11918 005b5c1
draft up to and including routing strategy
sarah11918 bdc15e4
draft up to fallback
sarah11918 34d2aee
full page draft
sarah11918 18cb22b
Apply suggestions from code review
sarah11918 70ba4f6
replace all lang with locale
sarah11918 43eb738
replace all pt_BR with pt-br
sarah11918 54f26f9
feedback from Ema re file structure
sarah11918 b74f640
Sarah editing pass
sarah11918 840b382
Yan!
sarah11918 8d1ae7b
Merge branch 'main' into feat/experimental-i18n-routing
sarah11918 1b7a33b
Update src/content/docs/en/guides/internationalization.mdx
sarah11918 dd4e916
link fix
sarah11918 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,290 @@ | ||
--- | ||
title: Internationalization | ||
description: Learn how to use i18n routing in Astro. | ||
i18nReady: false | ||
--- | ||
|
||
import FileTree from '~/components/FileTree.astro' | ||
import Since from '~/components/Since.astro' | ||
|
||
Astro's internationalization (i18n) features allow you to adapt your project for an international audience. | ||
|
||
|
||
## i18n Routing (Experimental) | ||
|
||
<Since v="3.5.0" /> | ||
|
||
Astro's experimental i18n routing allows you to add your multilingual content with support for configuring a default language, computing relative page URLs, and accepting preferred languages provided by your visitor's browser. You can also specify fallback languages on a per-language basis so that your visitors can always be directed to existing content on your site. | ||
|
||
This routing API helps you generate, use, and verify the URLs that your multi-language site produces. Check back and update regularly for the latest changes as this API continues to develop! | ||
|
||
|
||
### Configure i18n routing | ||
|
||
1. Enable the experimental routing option by adding an `i18n` object to your Astro configuration with a [default location (`defaultLocale`) and a list of all languages to support (`locales`)](#defaultlocale-and-locales): | ||
|
||
```js title="astro.config.mjs" | ||
import { defineConfig } from "astro/config" | ||
export default defineConfig({ | ||
experimental: { | ||
i18n: { | ||
defaultLocale: "en", | ||
locales: ["en", "es", "pt-br"] | ||
} | ||
} | ||
}) | ||
``` | ||
|
||
2. Choose and configure a [`routingStrategy`](#routingstrategy) based on the desired URL path for your `defaultLocale`: | ||
|
||
- `"prefix-other-locales"` (default): URLs in your default language will **not** have a `/[locale]/` prefix. All other locales will. | ||
|
||
- `"prefix-always"`: All URLs, including your default language, will have a `/[locale]/` prefix. | ||
|
||
```js title="astro.config.mjs" ins={7} | ||
import { defineConfig } from "astro/config" | ||
export default defineConfig({ | ||
experimental: { | ||
i18n: { | ||
defaultLocale: "en", | ||
locales: ["es", "en", "fr"], | ||
routingStrategy: "prefix-always" | ||
} | ||
} | ||
}) | ||
``` | ||
|
||
3. Organize your content folders with localized content by language. Your folder names must match the items in `locales` exactly, and your folder organization must match the URL paths chosen for your [`routingStrategy`](#routingstrategy). | ||
|
||
Include a localized folder for your `defaultLocale` only if you configure `prefix-always` to show a localized URL path. | ||
|
||
<FileTree> | ||
- src | ||
- pages | ||
- about.astro | ||
- index.astro | ||
- es | ||
- about.astro | ||
- index.astro | ||
- pt-br | ||
- about.astro | ||
- index.astro | ||
</FileTree> | ||
|
||
:::note | ||
The localized folders do not need to be at the root of the `/pages/` folder. | ||
|
||
Create individual `/[locale]/` folders anywhere within `src/pages/` and Astro's [file-based routing](/en/core-concepts/routing/) will create your pages at corresponding URL paths. | ||
::: | ||
|
||
4. With i18n routing configured, you can now compute links to pages within your site using the [`getRelativeLocaleURL()`](#getrelativelocaleurl) helper available from the [`astro:i18n` module](#virtual-module-astroi18n). This will always provide the correct, localized route and can help you correctly use, or check, URLs on your site. You can also still write the links manually. | ||
|
||
```astro title="src/pages/es/index.astro" | ||
--- | ||
import { getRelativeLocaleUrl } from 'astro:i18n'; | ||
|
||
// defaultLocale is "es" | ||
const aboutURL=getRelativeLocaleUrl("es", "about"); | ||
--- | ||
|
||
<a href="/get-started/">¡Vamos!</a> | ||
<a href={getRelativeLocaleUrl('es', 'blog')}>Blog</a> | ||
<a href={aboutURL}>Acerca</a> | ||
``` | ||
|
||
### `routingStrategy` | ||
|
||
Astro's built-in file-based routing automatically creates URL routes for you based on your file structure within `src/pages/`. When you configure i18n routing, the `routingStrategy` value now allows you to specify your file structure (and corresponding URL paths generated) in order to use helper functions to generate, use, and verify the routes in your project. | ||
|
||
#### `'prefix-other-locales'` | ||
|
||
```js title="astro.config.mjs" ins={7} | ||
import { defineConfig } from "astro/config" | ||
export default defineConfig({ | ||
experimental: { | ||
i18n: { | ||
defaultLocale: "en", | ||
locales: ["es", "en", "fr"], | ||
routingStrategy: "prefix-other-locales" | ||
} | ||
} | ||
}) | ||
``` | ||
|
||
This is the **default** value. Set this option when URLs in your default language will **not** have a `/[locale]/` prefix and files in your default language exist at the root of `src/pages/`. | ||
|
||
- `src/pages/blog.astro` will produce the route `example.com/blog/` | ||
- `src/pages/fr/blog.astro` will produce the route `example.com/fr/blog/` | ||
- If there is no file at `src/pages/es/blog.astro`, then the route `example.com/es/blog/` will 404 unless you specify a [fallback strategy](#fallback). | ||
|
||
|
||
#### `'prefix-always'` | ||
|
||
```js title="astro.config.mjs" ins={7} | ||
import { defineConfig } from "astro/config" | ||
export default defineConfig({ | ||
experimental: { | ||
i18n: { | ||
defaultLocale: "en", | ||
locales: ["es", "en", "fr"], | ||
routingStrategy: "prefix-always" | ||
} | ||
} | ||
}) | ||
``` | ||
|
||
Set this option when all routes will have their `/locale/` prefix in their URL and when all page content files, including those for your `defaultLocale`, exist in a localized folder: | ||
|
||
<FileTree> | ||
- src | ||
- pages | ||
- index.astro | ||
- en | ||
- index.astro | ||
- about.astro | ||
- es | ||
- about.astro | ||
- index.astro | ||
- pt-br | ||
- about.astro | ||
- index.astro | ||
</FileTree> | ||
|
||
- URLs without a local prefix, (e.g. `example.com/blog/`) will return a 404 (not found) status code. | ||
|
||
### `defaultLocale` and `locales` | ||
|
||
Both a default language ([`defaultLocale`](/en/reference/configuration-reference/#experimentali18ndefaultlocale)) and a list of all supported languages ([`locales`](/en/reference/configuration-reference/#experimentali18nlocales)) must be specified in your `i18n` routing configuration. | ||
|
||
Each language must be a string (e.g. `"fr"`, `"pt-br"`), but no particular language format or syntax is enforced while this feature is still experimental and under development. This may be subject to change in future versions. | ||
|
||
Your `/[locale]/` folder names must match exactly the `locales` in the list, and your [routing strategy](#routingstrategy) must correspond to whether or not you have a localized folder for your default language. Every other supported language must have its own localized folder. | ||
|
||
Depending on your deploy host, you may discover transformations in URL paths, so check your deployed site to determine the best syntax for your project. | ||
|
||
### Browser language detection | ||
|
||
Astro's i18n routing combined with one of Astro's [on-demand server rendering modes (`output:'server'` or `output:'hybrid'`)](/en/guides/server-side-rendering/) allow you to access two properties for browser language detection: `Astro.preferredLocale` and `Astro.preferredLocaleList`. | ||
|
||
These combine the browser's `Accept-Langauge` header, and your `locales` to automatically respect your visitor's preferred languages. | ||
|
||
- `Astro.preferredLocale`: Astro can compute a **preferred locale** for your visitor if their browser's preferred locale is included in your `locales` array. This value is undefined if no such match exists. | ||
|
||
|
||
- `Astro.preferredLocaleList`: An array of all locales that are both requested by the browser and supported by your website. This produces a list of all compatible languages between your site and your visitor. The value is `[]` if none of the browser's requested languages are found in your `locales` array. If the browser does not specify any preferred languages, then this value will be [`i18n.locales`]. | ||
|
||
|
||
### Fallback | ||
|
||
Astro's i18n routing allows you to configure a **fallback routing strategy**. When a page in one language doesn't exist (e.g. a page that is not yet translated), instead of displaying a 404 page, you can redirect a user from one locale to another on a per-language basis. This is useful when you do not yet have a page for every route, but you want to still provide some content to your visitors. | ||
|
||
For example, the configuration below sets `es` as the fallback locale for any missing `fr` routes. This means that a user visiting `example.com/fr/my-page/` will be redirected to and shown the content for `example.com/es/my-page/` instead of being taken to a 404 page when `src/pages/fr/my-page.astro` does not exist. | ||
|
||
```js title="astro.config.mjs" ins={6,7-9} | ||
import { defineConfig } from "astro/config" | ||
export default defineConfig({ | ||
experimental: { | ||
i18n: { | ||
defaultLocale: "en", | ||
locales: ["es", "en", "fr"], | ||
fallback: { | ||
fr: "es" | ||
} | ||
} | ||
} | ||
}) | ||
``` | ||
|
||
Astro will ensure that a page is built in `src/pages/fr` for every page that exists in `src/pages/es/`. If the page does not already exist, then a page with a redirect to the corresponding `es` route will be created. | ||
|
||
## Virtual module `astro:i18n` | ||
|
||
This module provides functions that can help you create URLs using your project's configured locales. | ||
|
||
Creating routes for your project with the i18n router will depend on certain configuration values you have set that affect your page routes. When creating routes with these functions, be sure to take into account your individual settings for: | ||
|
||
- [`base`](/en/reference/configuration-reference/#base) | ||
- [`trailingSlash`](/en/reference/configuration-reference/#trailingslash) | ||
- [`build.format`](/en/reference/configuration-reference/#buildformat) | ||
- [`site`](/en/reference/configuration-reference/#site) | ||
|
||
|
||
Also, note that the returned URLs created by these functions for your `defaultLocale` will reflect your `i18n.routingStrategy` configuration. | ||
|
||
URLs created when `prefix-always` is configured will include a `/lang/` path in the URL. URLs created with `prefix-other-locales` will not include a language prefix. | ||
|
||
### `getRelativeLocaleUrl()` | ||
|
||
|
||
`getRelativeLocaleUrl(locale: string, path: string, options?: GetLocaleOptions): string` | ||
|
||
Use this function to retrieve a relative path for a locale. If the locale doesn't exist, Astro throws an error. | ||
|
||
```astro | ||
--- | ||
getRelativeLocaleUrl("fr", ""); | ||
// returns /fr | ||
|
||
getRelativeLocaleUrl("fr", "getting-started"); | ||
// returns /fr/getting-started | ||
|
||
getRelativeLocaleUrl("fr_CA", "getting-started", { | ||
prependWith: "blog" | ||
}); | ||
// returns /blog/fr-ca/getting-started | ||
|
||
getRelativeLocaleUrl("fr_CA", "getting-started", { | ||
prependWith: "blog", | ||
normalizeLocale: false | ||
}); | ||
// returns /blog/fr_CA/getting-started | ||
--- | ||
``` | ||
|
||
### `getAbsoluteLocaleUrl()` | ||
|
||
`getAbsoluteLocaleUrl(locale: string, path: string, options?: GetLocaleOptions): string` | ||
|
||
|
||
Use this function to retrieve an absolute path for a locale when [`site`] has a value. If [`site`] isn't configured, the function returns a relative URL. If the locale doesn't exist, Astro throws an error. | ||
|
||
|
||
```astro | ||
--- | ||
// If `site` is set to be `https://example.com` | ||
|
||
getAbsoluteLocaleUrl("fr", ""); | ||
// returns https://example.com/fr | ||
|
||
getAbsoluteLocaleUrl("fr", "getting-started"); | ||
// returns https://example.com/fr/getting-started | ||
|
||
getAbsoluteLocaleUrl("fr_CA", "getting-started", { | ||
prependWith: "blog" | ||
}); | ||
// returns https://example.com/blog/fr-ca/getting-started | ||
|
||
getAbsoluteLocaleUrl("fr_CA", "getting-started", { | ||
prependWith: "blog", | ||
normalizeLocale: false | ||
}); | ||
// returns https://example.com/blog/fr_CA/getting-started | ||
--- | ||
``` | ||
|
||
### `getRelativeLocaleUrlList()` | ||
|
||
Use this like [`getRelativeLocaleUrl`](#getrelativelocaleurl) to return a list of relative paths for all the locales. | ||
|
||
|
||
`getRelativeLocaleUrlList(locale: string, options?: GetLocaleOptions): string[]` | ||
|
||
### `getAbsoluteLocaleUrlList()` | ||
|
||
`getAbsoluteLocaleUrlList(locale: string, options?: GetLocaleOptions): string[]` | ||
|
||
|
||
Use this like [`getAbsoluteLocaleUrl`](#getabsolutelocaleurl) to return a list of absolute paths for all the locales. | ||
|
||
[`site`]: /en/reference/configuration-reference/#site | ||
[`i18n.locales`]: /en/reference/configuration-reference/#experimentali18nlocales |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if it's worth mentioning it, but I leave the information here, and you decide: the locales coming from
Accept-Language
have their format, e.g.pt-BR
, although it's possible that developers decide to use a different format insidei18n.locales
, e.g.pt_BR
,pt-br
.Astro under to hood does some transformations (no need to mention this) and eventually will return the format specified in
i18n.locales
.So if
Accept-Language
isfr-CA;q=0.1, pt-BR;q=0.5"
, andi18n.locales
is["fr_CA", "pt_BR"]
:Astro.preferredLocale
is"pt_BR"
;Astro.preferredLocaleList
is["pt_BR", "fr_CA"]
, the order is important because it's kept fromAccept-Language
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me see what I can do here!