withastro / roadmap

Ideas, suggestions, and formal RFC proposals for the Astro project.
311 stars 30 forks source link

i18n routing RFC #734

Closed ematipico closed 11 months ago

ematipico commented 1 year ago

Summary

i18n routing support baked into Astro.

Links

ematipico commented 11 months ago

@ematipico

We landed to the conclusion that routing strategies should be applied only to pages: c93015d

This would solve most of the issues, but the routing is still pretty messed up which makes it hard to check. Especially the fallback part... I bet you saw the issues I opened.

I'll get to those issues, don't worry. It's completely fine to have issues during the experimental phase, and I thank you very much for being so adventurous to try this new feature.

itsmatteomanf commented 11 months ago

@ematipico

We landed to the conclusion that routing strategies should be applied only to pages: c93015d

This would solve most of the issues, but the routing is still pretty messed up which makes it hard to check. Especially the fallback part... I bet you saw the issues I opened.

I'll get to those issues, don't worry. It's completely fine to have issues during the experimental phase, and I thank you very much for being so adventurous to try this new feature.

I was mentioning those as they prevent a lot of testing as they fail in many cases, but I know you'll get there :)

itsmatteomanf commented 11 months ago

Oh, I might be adding a feature request or consideration at least.

If you want to add these tags, which are the i18n standard way of doing things you need all links, which you can get with the current APIs, but it becomes a pain to match those to the language that generated it.

Returning an array of value pairs or an object would be great. Mostly so you know which link corresponds to which locale.

jlarmstrongiv commented 11 months ago

I’ve seen several comments (1, 2, 3, 4, etc.) requesting that an Astro file render all locales

.
└── pages/
    ├── about.astro
    └── index.astro

instead of having Astro files for every locale

.
└── pages/
    ├── about.astro
    ├── index.astro
    ├── es/
    │   ├── about.astro
    │   └── index.astro
    └── fr/
        ├── about.astro
        └── index.astro

to avoid code duplication and fetch data from APIs and content collections, while still supporting the unprefixed defaultLocale

ematipico commented 11 months ago

@itsmatteomanf One question about your comment:

{
    locales: [
        {
            path: 'it',
            default: "it", // this is the one you want whenever you have to chose like a single value for the specific locale 
            lang: [ "it", "it-CH", "it-IT",  "it-SM",  "it-VA"] // these you use for like canonicals, matching routes, etc.
        }
    ]
}

What's the different between path and default? How should I use default and in which context?

itsmatteomanf commented 11 months ago

@ematipico

What's the different between path and default? How should I use default and in which context?

path is the actual URL path example.com/[lang]/blog, default is the default locale, it could be like it-CH, in that example.

It's like that so that you know which is the canonical (if it were just one language) or which is the locale you set your HTML to, e.g. <html lang="<default>">...</html>.

ematipico commented 11 months ago

Thank you @itsmatteomanf. Now that I understand what's meant for, maybe we can directly use the lang array. Since it's an array, we can leverage the order (from left to right), so we can also deliver a sense of priority of the languages needed by the user. This would be inline with how the Accept-Language header works. What do you think?

itsmatteomanf commented 11 months ago

Since it's an array, we can leverage the order (from left to right), so we can also deliver a sense of priority of the languages needed by the user.

Fine by me, @ematipico! I'm almost sure I wrote it somewhere, but I guess I didn't ahah

Maybe do expose that with a specific endpoint, just to make it clearer and easier, but not sure what scope and how big the APIs will be. It's kinda easy to do, anyway.

sandstrom commented 11 months ago

Some more feedback (you may have thought about it already).

Count and plural forms

English has two plural forms, but many languages have several. This is generally handled via a count param to the translation helper, e.g. t('login.status', { count: myInt }); and the following translation data:

login:
  heading: "Login statistics"
  status: 
    # some languages would have additional plural forms here
    one: "Last login ${count} day ago"
    other: "Last login ${count} days ago"

File formats

Instead of having .astro files, I'd recommend something like json or yaml.

Take some inspiration from the large localisation platforms:

stefanprobst commented 11 months ago

how would a language-switcher look like with the current astro:i18n api?

ematipico commented 11 months ago

how would a language-switcher look like with the current astro:i18n api?

Depending on your environment, use the list APIs: https://docs.astro.build/en/guides/internationalization/#getrelativelocaleurllist

Then map the result into a new array where you add the label of each locale. Then you should have all the information for a drop-down.

ematipico commented 11 months ago

Due to time and difficulty issues, we removed the domain support from this RFC. We will land domain support in Astro 4.* under a feature flag. https://github.com/withastro/roadmap/pull/734/commits/22d192b89b9b259d63410926a5612fdac145f99e

ematipico commented 11 months ago

Calling for consensus for this RFC. The period will last for 3 days and if there's no objections, this RFC will be merged.

ematipico commented 11 months ago

In https://github.com/withastro/roadmap/pull/734/commits/089253d317e4327ee4e3b93eb303ddc28859952b, I removed routingStrategy in favour of:

{
    routing: {
        strategy: "pathname",
        prefixDefaultLocale: true
    }
}

As discussed and proposed in https://github.com/withastro/roadmap/pull/734#issuecomment-1799126771

Also, myself and Chris had a chat, we changed the shape slightly to align it with the current one, and take advantage of the zod .transform function.

itsmatteomanf commented 11 months ago

Looks good to me, @ematipico.

The only thing I don't see, but may be there just not explained fully, is a way to get all locales, in the more granular configuration option, with their URLs, even if they are repeated.

This is very useful for the link alternate i18n configurations (and possibly even for the sitemap plugin).

ematipico commented 11 months ago

Looks good to me, @ematipico.

The only thing I don't see, but may be there just not explained fully, is a way to get all locales, in the more granular configuration option, with their URLs, even if they are repeated.

This is very useful for the link alternate i18n configurations (and possibly even for the sitemap plugin).

I'll be happy to add such APIs, could you propose something with the expected result? It would help to get the API right easily

itsmatteomanf commented 11 months ago

Looks good to me, @ematipico. The only thing I don't see, but may be there just not explained fully, is a way to get all locales, in the more granular configuration option, with their URLs, even if they are repeated. This is very useful for the link alternate i18n configurations (and possibly even for the sitemap plugin).

I'll be happy to add such APIs, could you propose something with the expected result? It would help to get the API right easily

Sorry, was on mobile...

Given a config file like this:

// astro.config.mjs
import {defineConfig} from "astro/config"
export default defineConfig({
    i18n: {
        defaultLocale: 'en',
        locales: ['en', 'es', 'fr', {
            path: "portugues",
            codes: ["pt", "pt-BR", "pt-AO"]
        }]
    }
})

Return something like this:

[
    ["en", "/blog"],
    ["es", "/es/blog"],
    ["fr", "/fr/blog"],
    ["pt", "/portugues/blog"],
    ["pt-BR", "/portugues/blog"],
    ["pt-AO", "/portugues/blog"]
]

Two issues I see, thinking about this now.

The current getRelativeLocaleUrlList() and getAbsoluteLocaleUrlList() don't associate a locale with the URL, making it hard to use in a <select> with a value of the locale or assign a label to the URL (how do I know which is first and get the label? I know it's probably the order I defined them in, but it's not stored anywhere, apart from the config file), and that would apply for this API, too.

Maybe an array of objects here, e.g. { locale: "pt-BR", path: "portugues", url: "/portugues/blog" }, is better?

And probably the same or similar in the other cases? 🤔

ematipico commented 11 months ago

I'll have to think about it, and we can definitely land a new API after we merge the RFC, there's nothing to prevent us from doing so.

zanhk commented 11 months ago

@ematipico Would be nice to have a feedback about code duplication (https://github.com/withastro/roadmap/pull/734#issuecomment-1817750144) as from a quick look on the RFC it's not clear to me how, after the implementation, the projects files should be structured, (I probably missed between the comments) is there a sample repo to showcase how the page structure will look like?

Thanks

ematipico commented 11 months ago

@ematipico Would be nice to have a feedback about code duplication (#734 (comment)) as from a quick look on the RFC it's not clear to me how, after the implementation, the projects files should be structured, (I probably missed between the comments) is there a sample repo to showcase how the page structure will look like?

Thanks

The RFC is meant for routing and fallback, not content (labels, dictionaries, etc.). We wanted to keep the first round of implementation small and provide as many utilises as possible so the community can build around them. Let's remember that nothing prevents us from adding more features. In fact, we already have a few features that we will add after this first RFC is merged,

The fallback system is one of the most significant changes we added because it adds patterns that weren't possible in Astro core before. Once we deem that feature stable enough, we could use the same logic to generate pages to avoid code duplication.

zanhk commented 11 months ago

@ematipico Thanks for the clarification

bluwy commented 11 months ago

Oops didn't notice this is merged, but anyways my comments aren't urgent, just some questions that I wonder if it's a typo or not.

eric-burel commented 7 months ago

Hey folks,

I am quite late to the party but I am currently discovering Astro, and I got a good knowledge of Next.js behavior and recent changes regarding i18n, so I'd like to share some feedback after spending ~1h playing with Astro i18n setup and reading the guide :

Most of these may just be documentation issue but I am also unsure about the architecture itself. Astro made a quite opinionated choice here, with a solution quite similar how Next.js worked before version 12.

However since that, Next.js has started considering i18n routing as a part of the broader family of personnalization via redirection. User-land middlewares, which are meant to run also for static pages, allow devs to get full control of i18n.

My feeling is that this is the inevitable direction that all frameworks are meant to take at some point. In Astro, to implement i18n routing, you seem to use an internal feature allowing to have middleware that can run for each HTTP request but also preserve the static nature of the page. Making this feature available to end user would allow them to take full control over i18n instead of relying on the framework provided implementation, but also cover similar personalization use cases.

Edit: got answers to my questions, for now using an Edge Middleware is fine (so I can have a tiny redirection server in front of my Astro server) and discussion should keep going there: https://github.com/withastro/roadmap/discussions/795