Add internationalization support with astro-i18next integration
- Implemented astro-i18next for multi-language support, including English, Dutch, and Italian. - Configured default locale and language fallback settings. - Defined routes for localized content in the configuration. - Updated package.json and package-lock.json to include new dependencies for i18next and related plugins.
This commit is contained in:
20
node_modules/astro-i18next/LICENSE.md
generated
vendored
Normal file
20
node_modules/astro-i18next/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Yassine Doghri
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
660
node_modules/astro-i18next/README.md
generated
vendored
Normal file
660
node_modules/astro-i18next/README.md
generated
vendored
Normal file
@@ -0,0 +1,660 @@
|
||||
<div align="center">
|
||||
|
||||
# 🧪 astro-i18next <!-- omit in toc -->
|
||||
|
||||
An [astro](https://astro.build/) integration of
|
||||
[i18next](https://www.i18next.com/) + some utility components to help you
|
||||
translate your astro websites!
|
||||
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[![npm-badge]][npm] [![build-badge]][build] [![codecov-badge]][codecov] [![license-badge]][license] [![contributions-badge]][contributions] [![semantic-release-badge]][semantic-release] [![stars-badge]][stars]
|
||||
|
||||
</div>
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> Status - 🚧 **Beta**
|
||||
>
|
||||
> [👉 **Road to v1.0.0**](https://github.com/yassinedoghri/astro-i18next/issues/19)
|
||||
>
|
||||
> You can use it, and feedback is more than welcome! Note that some breaking
|
||||
> changes may still be introduced during this phase as the goal for v1 is to get
|
||||
> the best possible DX for translating your Astro pages.
|
||||
|
||||
## Examples
|
||||
|
||||
| Example | Status |
|
||||
| ------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| [SSG - **Basics**](examples/basics) | [![example-up-badge]](examples/basics) |
|
||||
| [SSR - **Node**](examples/node) | [![example-up-badge]](examples/node) |
|
||||
| [**React**](examples/react) | [![example-up-badge]](examples/react) |
|
||||
| [SSR - **Netlify**](examples/netlify) | [![example-down-badge]](examples/netlify) (https://github.com/yassinedoghri/astro-i18next/issues/26) |
|
||||
| SSR - **Deno** | [![example-down-badge]](examples/basics) (https://github.com/yassinedoghri/astro-i18next/issues/55) |
|
||||
|
||||
- [Examples](#examples)
|
||||
- [🚀 Getting started](#-getting-started)
|
||||
- [1. Install](#1-install)
|
||||
- [2. Configure](#2-configure)
|
||||
- [3. Start translating](#3-start-translating)
|
||||
- [💻 CLI commands](#-cli-commands)
|
||||
- [generate](#generate)
|
||||
- [🔄 Translate Routes](#-translate-routes)
|
||||
- [📦 Utility components](#-utility-components)
|
||||
- [Trans component](#trans-component)
|
||||
- [LanguageSelector component](#languageselector-component)
|
||||
- [HeadHrefLangs component](#headhreflangs-component)
|
||||
- [📦 Utility functions](#-utility-functions)
|
||||
- [interpolate function](#interpolate-function)
|
||||
- [localizePath function](#localizepath-function)
|
||||
- [localizeUrl function](#localizeurl-function)
|
||||
- [👀 Going further](#-going-further)
|
||||
- [Namespaces](#namespaces)
|
||||
- [AstroI18nextConfig Props](#astroi18nextconfig-props)
|
||||
- [✨ Contributors](#-contributors)
|
||||
- [❤️ Acknowledgments](#️-acknowledgments)
|
||||
- [📜 License](#-license)
|
||||
|
||||
## 🚀 Getting started
|
||||
|
||||
### 1. Install
|
||||
|
||||
```bash
|
||||
npm install astro-i18next
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
pnpm add astro-i18next
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
yarn add astro-i18next
|
||||
```
|
||||
|
||||
### 2. Configure
|
||||
|
||||
1. Add `astro-i18next` to your `astro.config.mjs`:
|
||||
|
||||
```js
|
||||
import { defineConfig } from "astro/config";
|
||||
import astroI18next from "astro-i18next";
|
||||
|
||||
export default defineConfig({
|
||||
integrations: [astroI18next()],
|
||||
});
|
||||
```
|
||||
|
||||
2. Configure `astro-i18next` in your `astro-i18next.config.mjs` file:
|
||||
|
||||
```js
|
||||
/** @type {import('astro-i18next').AstroI18nextConfig} */
|
||||
export default {
|
||||
defaultLocale: "en",
|
||||
locales: ["en", "fr"],
|
||||
};
|
||||
```
|
||||
|
||||
ℹ️ Your `astro-i18next` config file can be a javascript (`.js` | `.mjs` |
|
||||
`.cjs`) or typescript (`.ts` | `.mts` | `.cts`) file.
|
||||
|
||||
ℹ️ For a more advanced configuration, see the
|
||||
[AstroI18nextConfig props](#astroi18nextconfig-props).
|
||||
|
||||
3. By default, `astro-i18next` expects your translations to be organized inside
|
||||
your
|
||||
[astro's `publicDir`](https://docs.astro.build/en/reference/configuration-reference/#publicdir),
|
||||
in a `locales` folder:
|
||||
|
||||
```bash
|
||||
public
|
||||
└── locales # create this folder to store your translation strings
|
||||
├── en
|
||||
| └── translation.json
|
||||
└── fr
|
||||
└── translation.json
|
||||
```
|
||||
|
||||
ℹ️ `astro-i18next` loads your translation files both server-side and
|
||||
client-side using
|
||||
[i18next-fs-backend](https://github.com/i18next/i18next-fs-backend) and
|
||||
[i18next-http-backend](https://github.com/i18next/i18next-http-backend)
|
||||
plugins.
|
||||
|
||||
ℹ️ You may choose to organize your translations into multiple files instead
|
||||
of a single file per locale [using namespaces](#namespaces).
|
||||
|
||||
### 3. Start translating
|
||||
|
||||
You may now start translating your pages by using
|
||||
[i18next's `t` function](https://www.i18next.com/overview/api#t) or the
|
||||
[Trans component](#trans-component) depending on your needs.
|
||||
|
||||
Here's a quick tutorial to get you going:
|
||||
|
||||
1. Use translation keys in your Astro pages
|
||||
|
||||
```astro
|
||||
---
|
||||
// src/pages/index.astro
|
||||
import i18next, { t } from "i18next";
|
||||
import { Trans, HeadHrefLangs } from "astro-i18next/components";
|
||||
---
|
||||
|
||||
<html lang={i18next.language}>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>{t("site.title")}</title>
|
||||
<meta name="description" content={t("site.description")} />
|
||||
<HeadHrefLangs />
|
||||
</head>
|
||||
<body>
|
||||
<h1>{t("home.title")}</h1>
|
||||
<p>
|
||||
<Trans i18nKey="home.subtitle">
|
||||
This is a <em>more complex</em> string to translate, mixed with <strong
|
||||
>html elements
|
||||
</strong> such as <a href="https://example.com/">a cool link</a>!
|
||||
</Trans>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```json
|
||||
// public/locales/en/translation.json
|
||||
{
|
||||
"site": {
|
||||
"title": "My awesome website!",
|
||||
"description": "Here is the description of my awesome website!"
|
||||
},
|
||||
"home": {
|
||||
"title": "Welcome to my awesome website!",
|
||||
"subtitle": "This is a <0>more complex</0> string to translate, mixed with <1>html elements</1>, such as a <2>a cool link</2>!"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
// public/locales/fr/translation.json
|
||||
{
|
||||
"site": {
|
||||
"title": "Mon super site web !",
|
||||
"description": "Voici la description de mon super site web !"
|
||||
},
|
||||
"home": {
|
||||
"title": "Bienvenue sur mon super site web !",
|
||||
"subtitle": "Ceci est une chaine de charactères <0>plus compliquée</0> à traduire, il y a des <1>éléments html</1>, comme <2>un super lien</2> par exemple !"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Create localized pages using the [generate command](#generate)
|
||||
|
||||
```bash
|
||||
npx astro-i18next generate
|
||||
```
|
||||
|
||||
3. You're all set! Have fun translating and generate localized pages as you go
|
||||
🚀
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> For a real world example, see the [website](./website/) or check out the
|
||||
> [examples](./examples/).
|
||||
|
||||
---
|
||||
|
||||
## 💻 CLI commands
|
||||
|
||||
### generate
|
||||
|
||||
```bash
|
||||
npx astro-i18next generate
|
||||
```
|
||||
|
||||
This command will generate localized pages depending on your config and set
|
||||
i18next's language change on each page.
|
||||
|
||||
For instance, with `locales = ["en", "fr", "es"]`, and `"en"` being the default
|
||||
locale and having:
|
||||
|
||||
```bash
|
||||
src
|
||||
└── pages
|
||||
├── about.astro
|
||||
└── index.astro
|
||||
```
|
||||
|
||||
👇 Running `npx astro-i18next generate` will create the following pages
|
||||
|
||||
```bash
|
||||
src
|
||||
└── pages
|
||||
├── es
|
||||
| ├── about.astro
|
||||
| └── index.astro
|
||||
├── fr
|
||||
| ├── about.astro
|
||||
| └── index.astro
|
||||
├── about.astro
|
||||
└── index.astro
|
||||
```
|
||||
|
||||
## 🔄 Translate Routes
|
||||
|
||||
`astro-i18next` let's you translate your pages routes for each locale!
|
||||
|
||||
For instance, with support for 3 locales (`en`, `fr`, `es`), `en` being the
|
||||
default and the following pages:
|
||||
|
||||
```bash
|
||||
src
|
||||
└── pages
|
||||
├── about.astro
|
||||
├── contact-us.astro
|
||||
└── index.astro
|
||||
```
|
||||
|
||||
1. Set route mappings in your `astro-i18next` config:
|
||||
|
||||
```js
|
||||
/** @type {import('astro-i18next').AstroI18nextConfig} */
|
||||
export default {
|
||||
defaultLocale: "en",
|
||||
locales: ["en", "fr", "es"],
|
||||
routes: {
|
||||
fr: {
|
||||
"about": "a-propos",
|
||||
"contact-us": "contactez-nous",
|
||||
"products": {
|
||||
"index": "produits",
|
||||
"categories": "categories",
|
||||
}
|
||||
}
|
||||
es: {
|
||||
"about": "a-proposito",
|
||||
"contact-us": "contactenos",
|
||||
"products": {
|
||||
"index": "productos",
|
||||
"categories": "categorias",
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
2. Generate your localized pages using the [generate CLI command](#generate),
|
||||
they will be translated for you!
|
||||
|
||||
```bash
|
||||
src
|
||||
└── pages
|
||||
├── es
|
||||
| ├── productos
|
||||
| | ├── categorias.astro
|
||||
| | └── index.astro
|
||||
| ├── a-proposito.astro
|
||||
| ├── contactenos.astro
|
||||
| └── index.astro
|
||||
├── fr
|
||||
| ├── produits
|
||||
| | ├── categories.astro
|
||||
| | └── index.astro
|
||||
| ├── a-propos.astro
|
||||
| ├── contactez-nous.astro
|
||||
| └── index.astro
|
||||
├── products
|
||||
| ├── categories.astro
|
||||
| └── index.astro
|
||||
├── about.astro
|
||||
├── contact-us.astro
|
||||
└── index.astro
|
||||
```
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> The [localizePath](#localizepath-function) and
|
||||
> [localizeUrl](#localizeurl-function) utility functions will retrieve the
|
||||
> correct route based on your mappings.
|
||||
|
||||
---
|
||||
|
||||
## 📦 Utility components
|
||||
|
||||
### Trans component
|
||||
|
||||
A component that takes care of interpolating its children with the translation
|
||||
strings. Inspired by
|
||||
[react-i18next's Trans component](https://react.i18next.com/latest/trans-component).
|
||||
|
||||
```astro
|
||||
---
|
||||
import { Trans } from "astro-i18next/components";
|
||||
---
|
||||
|
||||
<Trans i18nKey="superCoolKey">
|
||||
An <a href="https://astro.build" title="Astro website">astro</a> integration of
|
||||
<a href="https://www.i18next.com/" title="i18next website">i18next</a> and utility
|
||||
components to help you translate your astro websites!
|
||||
</Trans>
|
||||
```
|
||||
|
||||
```json
|
||||
// fr.json
|
||||
{
|
||||
"superCoolKey": "Une intégration <0>astro</0> d'<1>i18next</1> + quelques composants utilitaires pour vous aider à traduire vos sites astro !"
|
||||
}
|
||||
```
|
||||
|
||||
#### Trans Props
|
||||
|
||||
| Prop name | Type (default) | Description |
|
||||
| --------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| i18nKey | ?string (undefined) | Internationalization key to interpolate to. Can contain the namespace by prepending it in the form 'ns:key' (depending on i18next.options.nsSeparator). If omitted, a key is automatically generated using the content of the element. |
|
||||
| ns | ?string (undefined) | Namespace to use. May also be embedded in i18nKey but not recommended when used in combination with natural language keys. |
|
||||
|
||||
### LanguageSelector component
|
||||
|
||||
Unstyled custom select component to choose amongst supported locales.
|
||||
|
||||
```astro
|
||||
---
|
||||
import { LanguageSelector } from "astro-i18next/components";
|
||||
---
|
||||
|
||||
<LanguageSelector showFlag={true} class="my-select-class" />
|
||||
|
||||
<!-- LanguageSelector with custom language naming -->
|
||||
<LanguageSelector
|
||||
showFlag={true}
|
||||
languageMapping={{ en: "Anglais" }}
|
||||
class="my-select-class"
|
||||
/>
|
||||
```
|
||||
|
||||
#### LanguageSelector Props
|
||||
|
||||
| Prop name | Type (default) | Description |
|
||||
| --------------- | --------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| showFlag | ?boolean (`false`) | Choose to display the language emoji before language name |
|
||||
| languageMapping | ?object (`undefined`) | Rewrite language names by setting the locale as key and the wording of your choice as value |
|
||||
|
||||
### HeadHrefLangs component
|
||||
|
||||
HTML tags to include in your page's `<head>` section to let search engines know
|
||||
about its language and region variants. To know more, see
|
||||
[Google's advanced localized versions](https://developers.google.com/search/docs/advanced/crawling/localized-versions#html).
|
||||
|
||||
```astro
|
||||
---
|
||||
import i18next from "i18next";
|
||||
import { HeadHrefLangs } from "astro-i18next/components";
|
||||
---
|
||||
|
||||
<html lang={i18next.language}>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>...</title>
|
||||
<meta name="description" content="..." />
|
||||
<HeadHrefLangs />
|
||||
</head>
|
||||
<body>...</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
The HeadHrefLangs component will generate all of the alternate links depending
|
||||
on the current url and supported locales.
|
||||
|
||||
For example, if you are on the `/about` page and support 3 locales (`en`, `fr`,
|
||||
`es`) with `en` being the default locale, this will render:
|
||||
|
||||
```html
|
||||
<link rel="alternate" hreflang="en" href="https://www.example.com/about/" />
|
||||
<link rel="alternate" hreflang="fr" href="https://www.example.com/fr/about/" />
|
||||
<link rel="alternate" hreflang="es" href="https://www.example.com/es/about/" />
|
||||
```
|
||||
|
||||
## 📦 Utility functions
|
||||
|
||||
### interpolate function
|
||||
|
||||
`interpolate(i18nKey: string, reference: string, namespace: string | null): string`
|
||||
|
||||
`astro-i18next` exposes the logic behind the Trans component, you may want to
|
||||
use it directly.
|
||||
|
||||
```ts
|
||||
import { interpolate } from "astro-i18next";
|
||||
|
||||
const interpolated = interpolate(
|
||||
"superCoolKey",
|
||||
'An <a href="https://astro.build" title="Astro website">astro</a> integration of <a href="https://www.i18next.com/" title="i18next website">i18next</a> and utility components to help you translate your astro websites!'
|
||||
);
|
||||
```
|
||||
|
||||
### localizePath function
|
||||
|
||||
`localizePath(path: string, locale: string | null = null, base: string = import.meta.env.BASE_URL): string`
|
||||
|
||||
Sets a path within a given locale. If the locale param is not specified, the
|
||||
current locale will be used.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> This should be used instead of hard coding paths to other pages. It will take
|
||||
> care of setting the right path depending on the locale you set.
|
||||
|
||||
```astro
|
||||
---
|
||||
import { localizePath } from "astro-i18next";
|
||||
import i18next from "i18next";
|
||||
|
||||
i18next.changeLanguage("fr");
|
||||
---
|
||||
|
||||
<a href={localizePath("/about")}>...</a>
|
||||
<!-- renders: <a href="/fr/about">...</a> -->
|
||||
```
|
||||
|
||||
### localizeUrl function
|
||||
|
||||
`localizeUrl(url: string, locale: string | null = null, base: string = import.meta.env.BASE_URL): string`
|
||||
|
||||
Sets a url within a given locale. If the locale param is not specified, the
|
||||
current locale will be used.
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> This should be used instead of hard coding urls for internal links. It will
|
||||
> take care of setting the right url depending on the locale you set.
|
||||
|
||||
```astro
|
||||
---
|
||||
import { localizeUrl } from "astro-i18next";
|
||||
import i18next from "i18next";
|
||||
|
||||
i18next.changeLanguage("fr");
|
||||
---
|
||||
|
||||
<a href={localizeUrl("https://www.example.com/about")}>...</a>
|
||||
<!-- renders: <a href="https://www.example.com/fr/about">...</a> -->
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 👀 Going further
|
||||
|
||||
### Namespaces
|
||||
|
||||
i18next allows you to organize your translation keys into
|
||||
[namespaces](https://www.i18next.com/principles/namespaces).
|
||||
|
||||
You can have as many namespaces as you wish, have one per page and one for
|
||||
common translation strings for example:
|
||||
|
||||
```bash
|
||||
public
|
||||
├-- locales
|
||||
| |-- en
|
||||
| | |-- about.json # "about" namespace
|
||||
| | |-- common.json # "common" namespace
|
||||
| | └-- home.json # "home" namespace
|
||||
| └-- fr # same files in other locale folders
|
||||
src
|
||||
└-- pages
|
||||
|-- about.astro
|
||||
└-- index.astro
|
||||
```
|
||||
|
||||
1. It can easily be setup using the `namespaces` and `defaultNamespace` keys,
|
||||
like so:
|
||||
|
||||
```ts
|
||||
/** @type {import('astro-i18next').AstroI18nextConfig} */
|
||||
export default {
|
||||
defaultLocale: "en",
|
||||
locales: ["en", "fr"],
|
||||
namespaces: ["about", "common", "home"],
|
||||
defaultNamespace: "common",
|
||||
};
|
||||
```
|
||||
|
||||
2. Load the namespace globally using `i18next.setDefaultNamespace(ns: string)`
|
||||
or specify it in the `t` function or the `Trans` component:
|
||||
|
||||
```astro
|
||||
---
|
||||
import { t, setDefaultNamespace } from "i18next";
|
||||
import { Trans } from "astro-i18next/components";
|
||||
|
||||
setDefaultNamespace("home");
|
||||
---
|
||||
|
||||
<h1>{t("myHomeTitle")}</h1>
|
||||
<p>
|
||||
<Trans i18nKey="myHomeDescription">
|
||||
This translation is loaded from the default <strong>home</strong> namespace!
|
||||
</Trans>
|
||||
</p>
|
||||
<p>
|
||||
<Trans i18nKey="myCommonCTA" ns="common">
|
||||
This translation is loaded from the <strong>common</strong> namespace!
|
||||
</Trans>
|
||||
</p>
|
||||
<!-- t function loads the "buttonCTA" key from the "common" namespace -->
|
||||
<button>{t("common:buttonCTA")}</button>
|
||||
```
|
||||
|
||||
### AstroI18nextConfig Props
|
||||
|
||||
`astro-i18next`'s goal is to abstract most of the configuration for you so that
|
||||
you don't have to think about it. Just focus on translating!
|
||||
|
||||
Though if you'd like to go further in customizing i18next, feel free to tweak
|
||||
your config!
|
||||
|
||||
| Prop name | Type (default) | Description |
|
||||
| -------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| defaultLocale | `string` (undefined) | The default locale for your website. |
|
||||
| locales | `string[]` (undefined) | Your website's supported locales. |
|
||||
| namespaces | `string` or `string[]` ('translation') | String or array of namespaces to load. |
|
||||
| defaultNamespace | `string` (translation') | Default namespace used if not passed to the translation function. |
|
||||
| load | `Array<"server" or "client">` (`["server"]`) | Load i18next on server side only, client side only or both. |
|
||||
| resourcesBasePath | `?string` | Set base path for i18next resources. Defaults to `/locales`. |
|
||||
| i18nextServer | `?InitOptions` | The i18next server side configuration. See [i18next's documentation](https://www.i18next.com/overview/configuration-options). |
|
||||
| i18nextServerPlugins | `?{[key: string]: string}` (`{}`) | Set i18next server side plugins. See [available plugins](https://www.i18next.com/overview/plugins-and-utils). |
|
||||
| i18nextClient | `?InitOptions` | The i18next client side configuration . See [i18next's documentation](https://www.i18next.com/overview/configuration-options). |
|
||||
| i18nextClientPlugins | `?{[key: string]: string}` (`{}`) | Set i18next client side plugins. See [available plugins](https://www.i18next.com/overview/plugins-and-utils). |
|
||||
| routes | `[segment: string]: string or object`(`{}`) | The translations mapping for your routes. See [translate routes](#-translate-routes). |
|
||||
| showDefaultLocale | `boolean`(`false`) | Whether or not the defaultLocale should show up in the url just as other locales. |
|
||||
|
||||
## ✨ Contributors
|
||||
|
||||
Thanks goes to these wonderful people
|
||||
([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://yassinedoghri.com/"><img src="https://avatars.githubusercontent.com/u/11021441?v=4?s=100" width="100px;" alt="Yassine Doghri"/><br /><sub><b>Yassine Doghri</b></sub></a><br /><a href="https://github.com/yassinedoghri/astro-i18next/commits?author=yassinedoghri" title="Code">💻</a> <a href="https://github.com/yassinedoghri/astro-i18next/commits?author=yassinedoghri" title="Documentation">📖</a> <a href="#ideas-yassinedoghri" title="Ideas, Planning, & Feedback">🤔</a> <a href="#design-yassinedoghri" title="Design">🎨</a> <a href="#example-yassinedoghri" title="Examples">💡</a> <a href="#maintenance-yassinedoghri" title="Maintenance">🚧</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://gdevs.io/"><img src="https://avatars.githubusercontent.com/u/10165264?v=4?s=100" width="100px;" alt="Davide Ceschia"/><br /><sub><b>Davide Ceschia</b></sub></a><br /><a href="https://github.com/yassinedoghri/astro-i18next/commits?author=killpowa" title="Code">💻</a> <a href="https://github.com/yassinedoghri/astro-i18next/issues?q=author%3Akillpowa" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/preetamslot"><img src="https://avatars.githubusercontent.com/u/5420582?v=4?s=100" width="100px;" alt="preetamslot"/><br /><sub><b>preetamslot</b></sub></a><br /><a href="https://github.com/yassinedoghri/astro-i18next/issues?q=author%3Apreetamslot" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/dklymenko"><img src="https://avatars.githubusercontent.com/u/1391015?v=4?s=100" width="100px;" alt="Dmytro"/><br /><sub><b>Dmytro</b></sub></a><br /><a href="https://github.com/yassinedoghri/astro-i18next/issues?q=author%3Admythro" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://duskmoon314.com/"><img src="https://avatars.githubusercontent.com/u/20477228?v=4?s=100" width="100px;" alt="Campbell He"/><br /><sub><b>Campbell He</b></sub></a><br /><a href="https://github.com/yassinedoghri/astro-i18next/issues?q=author%3Aduskmoon314" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://t.me/mellkam"><img src="https://avatars.githubusercontent.com/u/51422045?v=4?s=100" width="100px;" alt="MelKam"/><br /><sub><b>MelKam</b></sub></a><br /><a href="https://github.com/yassinedoghri/astro-i18next/commits?author=MellKam" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://webslc.com/"><img src="https://avatars.githubusercontent.com/u/34887287?v=4?s=100" width="100px;" alt="L1lith"/><br /><sub><b>L1lith</b></sub></a><br /><a href="https://github.com/yassinedoghri/astro-i18next/issues?q=author%3AL1lith" title="Bug reports">🐛</a> <a href="#ideas-L1lith" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Anomander43"><img src="https://avatars.githubusercontent.com/u/14289502?v=4?s=100" width="100px;" alt="Anomander43"/><br /><sub><b>Anomander43</b></sub></a><br /><a href="https://github.com/yassinedoghri/astro-i18next/commits?author=Anomander43" title="Documentation">📖</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dschoeni"><img src="https://avatars.githubusercontent.com/u/1913623?v=4?s=100" width="100px;" alt="Dominik Schöni"/><br /><sub><b>Dominik Schöni</b></sub></a><br /><a href="https://github.com/yassinedoghri/astro-i18next/commits?author=dschoeni" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dallyh"><img src="https://avatars.githubusercontent.com/u/6968534?v=4?s=100" width="100px;" alt="Dalibor Hon"/><br /><sub><b>Dalibor Hon</b></sub></a><br /><a href="https://github.com/yassinedoghri/astro-i18next/commits?author=dallyh" title="Code">💻</a> <a href="https://github.com/yassinedoghri/astro-i18next/issues?q=author%3Adallyh" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aquaminer"><img src="https://avatars.githubusercontent.com/u/17113289?v=4?s=100" width="100px;" alt="Oleksii Lozoviahin"/><br /><sub><b>Oleksii Lozoviahin</b></sub></a><br /><a href="https://github.com/yassinedoghri/astro-i18next/commits?author=aquaminer" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://talale.it/"><img src="https://avatars.githubusercontent.com/u/68308554?v=4?s=100" width="100px;" alt="Alessandro Talamona"/><br /><sub><b>Alessandro Talamona</b></sub></a><br /><a href="https://github.com/yassinedoghri/astro-i18next/issues?q=author%3AxTalAlex" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jkjustjoshing"><img src="https://avatars.githubusercontent.com/u/813192?v=4?s=100" width="100px;" alt="Josh Kramer"/><br /><sub><b>Josh Kramer</b></sub></a><br /><a href="https://github.com/yassinedoghri/astro-i18next/commits?author=jkjustjoshing" title="Code">💻</a> <a href="https://github.com/yassinedoghri/astro-i18next/issues?q=author%3Ajkjustjoshing" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Alexandre-Fernandez"><img src="https://avatars.githubusercontent.com/u/79476242?v=4?s=100" width="100px;" alt="Alexandre Fernandez"/><br /><sub><b>Alexandre Fernandez</b></sub></a><br /><a href="https://github.com/yassinedoghri/astro-i18next/commits?author=Alexandre-Fernandez" title="Code">💻</a> <a href="https://github.com/yassinedoghri/astro-i18next/issues?q=author%3AAlexandre-Fernandez" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-restore -->
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the
|
||||
[all-contributors](https://github.com/all-contributors/all-contributors)
|
||||
specification. Contributions of any kind welcome!
|
||||
|
||||
## ❤️ Acknowledgments
|
||||
|
||||
This wouldn't have been possible without the awesome work from the
|
||||
[Locize](https://locize.com/) and [Astro](https://astro.build/) teams.
|
||||
|
||||
Inspired by some of the greatly thought-out i18n implementations:
|
||||
|
||||
- [next-i18next](https://github.com/i18next/next-i18next)
|
||||
- [react-i18next](https://github.com/i18next/react-i18next)
|
||||
- [NextJS's Internationalized Routing](https://nextjs.org/docs/advanced-features/i18n-routing)
|
||||
|
||||
## 📜 License
|
||||
|
||||
Code released under the [MIT License](https://choosealicense.com/licenses/mit/).
|
||||
|
||||
Copyright (c) 2022-present, Yassine Doghri
|
||||
([@yassinedoghri](https://twitter.com/yassinedoghri))
|
||||
|
||||
[npm]: https://www.npmjs.com/package/astro-i18next
|
||||
[npm-badge]: https://img.shields.io/npm/v/astro-i18next
|
||||
[build]:
|
||||
https://github.com/yassinedoghri/astro-i18next/actions/workflows/publish.yml
|
||||
[build-badge]:
|
||||
https://img.shields.io/github/actions/workflow/status/yassinedoghri/astro-i18next/publish.yml
|
||||
[license]:
|
||||
https://github.com/yassinedoghri/astro-i18next/blob/develop/LICENSE.md
|
||||
[license-badge]:
|
||||
https://img.shields.io/github/license/yassinedoghri/astro-i18next?color=blue
|
||||
[contributions]: https://github.com/yassinedoghri/astro-i18next/issues
|
||||
[contributions-badge]:
|
||||
https://img.shields.io/badge/contributions-welcome-blueviolet.svg
|
||||
[semantic-release]: https://github.com/semantic-release/semantic-release
|
||||
[semantic-release-badge]:
|
||||
https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
|
||||
[stars]: https://github.com/yassinedoghri/astro-i18next/stargazers
|
||||
[stars-badge]:
|
||||
https://img.shields.io/github/stars/yassinedoghri/astro-i18next?style=social
|
||||
[codecov]: https://codecov.io/gh/yassinedoghri/astro-i18next
|
||||
[codecov-badge]:
|
||||
https://codecov.io/gh/yassinedoghri/astro-i18next/branch/develop/graph/badge.svg?token=IFWNB6UJDJ
|
||||
[example-up-badge]: https://img.shields.io/badge/status-up-brightgreen
|
||||
[example-down-badge]: https://img.shields.io/badge/status-down-red
|
411
node_modules/astro-i18next/dist/cli/index.js
generated
vendored
Executable file
411
node_modules/astro-i18next/dist/cli/index.js
generated
vendored
Executable file
File diff suppressed because one or more lines are too long
9
node_modules/astro-i18next/dist/index.js
generated
vendored
Normal file
9
node_modules/astro-i18next/dist/index.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
13
node_modules/astro-i18next/dist/types/cli/generate.d.ts
generated
vendored
Normal file
13
node_modules/astro-i18next/dist/types/cli/generate.d.ts
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { AstroI18nextConfig } from "../types";
|
||||
import { FileToGenerate } from "./utils";
|
||||
/**
|
||||
* Reads all files inside inputPath
|
||||
*
|
||||
* @param inputPath
|
||||
* @param locales
|
||||
* @param outputPath
|
||||
*/
|
||||
export declare const generate: (inputPath: string, defaultLocale: AstroI18nextConfig["defaultLocale"], locales: AstroI18nextConfig["locales"], showDefaultLocale?: boolean, flatRoutes?: AstroI18nextConfig["flatRoutes"], outputPath?: string) => {
|
||||
filesToGenerate: FileToGenerate[];
|
||||
timeToProcess: number;
|
||||
};
|
2
node_modules/astro-i18next/dist/types/cli/index.d.ts
generated
vendored
Normal file
2
node_modules/astro-i18next/dist/types/cli/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env node
|
||||
export {};
|
4
node_modules/astro-i18next/dist/types/cli/middlewares.d.ts
generated
vendored
Normal file
4
node_modules/astro-i18next/dist/types/cli/middlewares.d.ts
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import { MiddlewareFunction } from "yargs";
|
||||
import { GenerateArgs, GlobalArgs } from "./types";
|
||||
export declare const loadConfig: MiddlewareFunction<GlobalArgs & GenerateArgs>;
|
||||
export declare const normalizePath: MiddlewareFunction<GlobalArgs & GenerateArgs>;
|
7
node_modules/astro-i18next/dist/types/cli/transformer.d.ts
generated
vendored
Normal file
7
node_modules/astro-i18next/dist/types/cli/transformer.d.ts
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import ts from "typescript";
|
||||
/**
|
||||
* Traverse ts' AST to inject i18next's language switch
|
||||
* @param context
|
||||
* @returns
|
||||
*/
|
||||
export declare const transformer: ts.TransformerFactory<ts.SourceFile>;
|
9
node_modules/astro-i18next/dist/types/cli/types.d.ts
generated
vendored
Normal file
9
node_modules/astro-i18next/dist/types/cli/types.d.ts
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { AstroI18nextConfig } from "../types";
|
||||
export interface GlobalArgs {
|
||||
verbose: boolean;
|
||||
}
|
||||
export interface GenerateArgs {
|
||||
path: string;
|
||||
config: AstroI18nextConfig;
|
||||
output: string;
|
||||
}
|
40
node_modules/astro-i18next/dist/types/cli/utils.d.ts
generated
vendored
Normal file
40
node_modules/astro-i18next/dist/types/cli/utils.d.ts
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
import { PathsOutput } from "fdir";
|
||||
import ts from "typescript";
|
||||
import { AstroI18nextConfig } from "../types";
|
||||
export interface FileToGenerate {
|
||||
path: string;
|
||||
source: string;
|
||||
}
|
||||
export declare const doesStringIncludeFrontmatter: (source: string) => boolean;
|
||||
export declare const extractFrontmatterFromAstroSource: (source: string) => string;
|
||||
export declare const overwriteAstroFrontmatter: (source: string, frontmatter: string) => string;
|
||||
export declare const addDepthToRelativePath: (relativePath: string, depth?: number) => string;
|
||||
/**
|
||||
* file is hidden if its name or any of its parent folders start with an underscore
|
||||
*/
|
||||
export declare const isFileHidden: (filepath: string) => boolean;
|
||||
export declare const resolveRelativePathsLevel: (fileContents: string, fileDepth: number) => string;
|
||||
/**
|
||||
* parse frontmatter using typescript compiler
|
||||
*
|
||||
* @param source
|
||||
*/
|
||||
export declare const parseFrontmatter: (source: string) => ts.SourceFile;
|
||||
export declare const generateLocalizedFrontmatter: (tsNode: ts.SourceFile, locale: string) => string;
|
||||
/**
|
||||
* Crawls pages directory and returns all Astro pages
|
||||
* except for locale folders and excluded pages / directories (starting with underscore).
|
||||
* (https://docs.astro.build/en/core-concepts/routing/#excluding-pages)
|
||||
*
|
||||
* @param pagesDirectoryPath
|
||||
* @param childDirToCrawl will make the function crawl inside the given
|
||||
* `childDirToCrawl` (doesn't take paths, only dirname).
|
||||
*/
|
||||
export declare const getAstroPagesFullPaths: (pagesDirectoryPath: string, childDirToCrawl?: AstroI18nextConfig["defaultLocale"] | undefined, locales?: AstroI18nextConfig["locales"]) => PathsOutput;
|
||||
export declare const createFiles: (filesToGenerate: FileToGenerate[]) => void;
|
||||
/**
|
||||
* Resolves the right translated path based on
|
||||
* a given `astroFilePath` and a locale,
|
||||
* with the `routeTranslations` mapping.
|
||||
*/
|
||||
export declare const resolveTranslatedAstroPath: (astroFilePath: string, locale?: string | null, basePath?: string, flatRoutes?: AstroI18nextConfig["flatRoutes"]) => string;
|
11
node_modules/astro-i18next/dist/types/config.d.ts
generated
vendored
Normal file
11
node_modules/astro-i18next/dist/types/config.d.ts
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { AstroI18nextConfig, AstroI18nextGlobal } from "./types";
|
||||
export declare const AstroI18next: AstroI18nextGlobal;
|
||||
export declare const setAstroI18nextConfig: (config: AstroI18nextConfig) => void;
|
||||
export declare const astroI18nextConfigBuilder: (config: AstroI18nextConfig) => AstroI18nextConfig;
|
||||
/**
|
||||
* This will create a mapping of translated routes to search them easily.
|
||||
*
|
||||
* TODO: render all routes mappings in here (even those not translated),
|
||||
* this will help simplify utility functions logic
|
||||
*/
|
||||
export declare const flattenRoutes: (routes: AstroI18nextConfig["routes"], previous?: string[], translatedPrevious?: string[], prevResult?: AstroI18nextConfig["flatRoutes"]) => AstroI18nextConfig["flatRoutes"];
|
8
node_modules/astro-i18next/dist/types/index.d.ts
generated
vendored
Normal file
8
node_modules/astro-i18next/dist/types/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import { AstroIntegration } from "astro";
|
||||
import { AstroI18nextConfig, AstroI18nextOptions } from "./types";
|
||||
declare const _default: (options?: AstroI18nextOptions) => AstroIntegration;
|
||||
export default _default;
|
||||
export declare function initAstroI18next(config: AstroI18nextConfig): void;
|
||||
export { AstroI18next } from "./config";
|
||||
export { createReferenceStringFromHTML, detectLocaleFromPath, interpolate, localizePath, localizeUrl, } from "./utils";
|
||||
export { AstroI18nextConfig, AstroI18nextOptions } from "./types";
|
116
node_modules/astro-i18next/dist/types/types.d.ts
generated
vendored
Normal file
116
node_modules/astro-i18next/dist/types/types.d.ts
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
import { InitOptions } from "i18next";
|
||||
export interface AstroI18nextGlobal {
|
||||
config: AstroI18nextConfig;
|
||||
}
|
||||
export interface AstroI18nextOptions {
|
||||
/**
|
||||
* Path to your astro-i18next config file
|
||||
*
|
||||
* @default 'astro-i18next.config.js'
|
||||
*/
|
||||
configPath?: string;
|
||||
}
|
||||
export interface Routes {
|
||||
[segment: string]: string | Record<string, string | Routes>;
|
||||
}
|
||||
export interface Plugins {
|
||||
[importName: string]: string | null;
|
||||
}
|
||||
export interface AstroI18nextConfig {
|
||||
/**
|
||||
* The default locale for your website.
|
||||
*
|
||||
* @default "cimode"
|
||||
*/
|
||||
defaultLocale: string;
|
||||
/**
|
||||
* The locales that are supported by your website.
|
||||
*
|
||||
* @default []
|
||||
*/
|
||||
locales: string[];
|
||||
/**
|
||||
* String or array of namespaces to load
|
||||
*
|
||||
* @default "translation"
|
||||
*/
|
||||
namespaces?: string | string[];
|
||||
/**
|
||||
* Default namespace used if not passed to the translation function
|
||||
*
|
||||
* @default "translation"
|
||||
*/
|
||||
defaultNamespace?: string;
|
||||
/**
|
||||
* Load i18next on server side only, client side only or both.
|
||||
*
|
||||
* @default ["server"]
|
||||
*/
|
||||
load?: ("server" | "client")[];
|
||||
/**
|
||||
* Set base path for i18next resources.
|
||||
*
|
||||
* @default "/locales"
|
||||
*/
|
||||
resourcesBasePath?: string;
|
||||
/**
|
||||
* i18next server side config. See https://www.i18next.com/overview/configuration-options
|
||||
*/
|
||||
i18nextServer?: InitOptions;
|
||||
/**
|
||||
* i18next client side config. See https://www.i18next.com/overview/configuration-options
|
||||
*/
|
||||
i18nextClient?: InitOptions;
|
||||
/**
|
||||
* The translations for your routes.
|
||||
*
|
||||
* @default {}
|
||||
*/
|
||||
routes?: Routes;
|
||||
/**
|
||||
* Generated mappings based on the routes
|
||||
*
|
||||
* @default {}
|
||||
*/
|
||||
readonly flatRoutes?: Record<string, string>;
|
||||
/**
|
||||
* The display behaviour for the URL locale.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
showDefaultLocale?: boolean;
|
||||
/**
|
||||
* i18next server side plugins. See https://www.i18next.com/overview/plugins-and-utils
|
||||
*
|
||||
* Include the plugins with the key being the import name and the value being the plugin name.
|
||||
*
|
||||
* Eg.:
|
||||
* ```
|
||||
* {
|
||||
* "Backend": "i18next-fs-backend",
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
i18nextServerPlugins?: Plugins;
|
||||
/**
|
||||
* i18next client side plugins. See https://www.i18next.com/overview/plugins-and-utils
|
||||
*
|
||||
* Include the plugins with the key being the import name and the value being the plugin name.
|
||||
*
|
||||
* Eg.:
|
||||
* ```
|
||||
* {
|
||||
* "{initReactI18next}": "react-i18next",
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
i18nextClientPlugins?: Plugins;
|
||||
/**
|
||||
* Set the route matching behavior of the dev server. Choose from the following options:
|
||||
*
|
||||
* 'always' - Only match URLs that include a trailing slash (ex: "/foo/")
|
||||
* 'never' - Never match URLs that include a trailing slash (ex: "/foo")
|
||||
* 'ignore' - Match URLs regardless of whether a trailing "/" exists
|
||||
*/
|
||||
trailingSlash?: "always" | "never" | "ignore";
|
||||
}
|
35
node_modules/astro-i18next/dist/types/utils.d.ts
generated
vendored
Normal file
35
node_modules/astro-i18next/dist/types/utils.d.ts
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/// <reference types="node" />
|
||||
import load from "@proload/core";
|
||||
import { AstroI18nextConfig } from "./types";
|
||||
/**
|
||||
* Adapted from astro's tailwind integration:
|
||||
* https://github.com/withastro/astro/tree/main/packages/integrations/tailwind
|
||||
*/
|
||||
export declare const getUserConfig: (root: URL, configPath?: string) => Promise<load.Config<AstroI18nextConfig>>;
|
||||
/**
|
||||
* Moves the default locale in the first index
|
||||
*/
|
||||
export declare const moveDefaultLocaleToFirstIndex: (locales: string[], baseLocale: string) => void;
|
||||
/**
|
||||
* Interpolates a localized string (loaded with the i18nKey) to a given reference string.
|
||||
*/
|
||||
export declare const interpolate: (i18nKey: string, referenceString: string, namespace?: string | null) => string;
|
||||
/**
|
||||
* Creates a reference string from an HTML string. The reverse of interpolate(), for use
|
||||
* with <Trans> when not explicitly setting a key
|
||||
*/
|
||||
export declare const createReferenceStringFromHTML: (html: string) => string;
|
||||
export declare const handleTrailingSlash: (path: string, trailingSlash: AstroI18nextConfig["trailingSlash"]) => string;
|
||||
/**
|
||||
* Injects the given locale to a path
|
||||
*/
|
||||
export declare const localizePath: (path?: string, locale?: string | null, base?: string) => string;
|
||||
/**
|
||||
* Injects the given locale to a url
|
||||
*/
|
||||
export declare const localizeUrl: (url: string, locale?: string | null, base?: string) => string;
|
||||
/**
|
||||
* Returns the locale detected from a given path
|
||||
*/
|
||||
export declare const detectLocaleFromPath: (path: string) => string;
|
||||
export declare const deeplyStringifyObject: (obj: object | Array<any>) => string;
|
21
node_modules/astro-i18next/node_modules/i18next/LICENSE
generated
vendored
Normal file
21
node_modules/astro-i18next/node_modules/i18next/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2022 i18next
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
63
node_modules/astro-i18next/node_modules/i18next/README.md
generated
vendored
Normal file
63
node_modules/astro-i18next/node_modules/i18next/README.md
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
# i18next: learn once - translate everywhere [](https://twitter.com/intent/tweet?text=Awesome%20i18next:%20learn%20once%20-%20translate%20everywhere%20-%20the%20internationalization%20ecosystem%20&url=https://github.com/i18next/i18next&via=jamuhl&hashtags=i18n,javascript,dev)
|
||||
|
||||
[](https://circleci.com/gh/i18next/i18next)
|
||||
[](https://codeclimate.com/github/i18next/i18next)
|
||||
[](https://coveralls.io/github/i18next/i18next)
|
||||
[](https://packagequality.com/#?package=i18next)
|
||||
[](https://cdnjs.com/libraries/i18next)
|
||||
[](https://www.npmjs.com/package/i18next)
|
||||

|
||||
|
||||
i18next is a very popular internationalization framework for browser or any other javascript environment (eg. Node.js, Deno).
|
||||
|
||||

|
||||
|
||||
i18next provides:
|
||||
|
||||
- Flexible connection to [backend](https://www.i18next.com/overview/plugins-and-utils#backends) (loading translations via xhr, ...)
|
||||
- Optional [caching](https://www.i18next.com/how-to/caching), user [language detection](https://www.i18next.com/overview/plugins-and-utils#language-detector), ...
|
||||
- Proper [pluralizations](https://www.i18next.com/translation-function/plurals)
|
||||
- Translation [context](https://www.i18next.com/translation-function/context)
|
||||
- [Nesting](https://www.i18next.com/translation-function/nesting), [Variable replacement](https://www.i18next.com/translation-function/interpolation)
|
||||
- Flexibility: [Use it everywhere](https://www.i18next.com/overview/supported-frameworks)
|
||||
- Extensibility: eg. [sprintf](https://www.i18next.com/overview/plugins-and-utils#post-processors)
|
||||
- ...
|
||||
|
||||
For more information visit the website:
|
||||
|
||||
- [Getting started](https://www.i18next.com/overview/getting-started)
|
||||
- [Translation Functionality](https://www.i18next.com/translation-function/essentials)
|
||||
- [API](https://www.i18next.com/overview/api)
|
||||
|
||||
Our focus is providing the core to building a booming ecosystem. Independent of the building blocks you choose, be it react, angular or even good old jquery proper translation capabilities are just [one step away](https://www.i18next.com/overview/supported-frameworks).
|
||||
|
||||
### Documentation
|
||||
|
||||
The general i18next documentation is published on [www.i18next.com](https://www.i18next.com) and PR changes can be supplied [here](https://github.com/i18next/i18next-gitbook).
|
||||
|
||||
The react specific documentation is published on [react.i18next.com](https://react.i18next.com) and PR changes can be supplied [here](https://github.com/i18next/react-i18next-gitbook).
|
||||
|
||||
---
|
||||
|
||||
<h3 align="center">Gold Sponsors</h3>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://locize.com/" target="_blank">
|
||||
<img src="https://raw.githubusercontent.com/i18next/i18next/master/assets/locize_sponsor_240.gif" width="240px">
|
||||
</a>
|
||||
<a href="https://localistars.com/" target="_blank">
|
||||
<img src="https://raw.githubusercontent.com/i18next/i18next/master/assets/localistars_sponsor_240.gif" width="240px">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
**From the creators of i18next: localization as a service - locize.com**
|
||||
|
||||
A translation management system built around the i18next ecosystem - [locize.com](https://locize.com).
|
||||
|
||||

|
||||
|
||||
With using [locize](https://locize.com/?utm_source=i18next_readme&utm_medium=github) you directly support the future of i18next.
|
||||
|
||||
---
|
2514
node_modules/astro-i18next/node_modules/i18next/dist/cjs/i18next.js
generated
vendored
Normal file
2514
node_modules/astro-i18next/node_modules/i18next/dist/cjs/i18next.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2657
node_modules/astro-i18next/node_modules/i18next/dist/esm/i18next.bundled.js
generated
vendored
Normal file
2657
node_modules/astro-i18next/node_modules/i18next/dist/esm/i18next.bundled.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2515
node_modules/astro-i18next/node_modules/i18next/dist/esm/i18next.js
generated
vendored
Normal file
2515
node_modules/astro-i18next/node_modules/i18next/dist/esm/i18next.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/astro-i18next/node_modules/i18next/dist/esm/package.json
generated
vendored
Normal file
1
node_modules/astro-i18next/node_modules/i18next/dist/esm/package.json
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"type":"module","version":"22.5.1"}
|
2642
node_modules/astro-i18next/node_modules/i18next/dist/umd/i18next.js
generated
vendored
Normal file
2642
node_modules/astro-i18next/node_modules/i18next/dist/umd/i18next.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/astro-i18next/node_modules/i18next/dist/umd/i18next.min.js
generated
vendored
Normal file
1
node_modules/astro-i18next/node_modules/i18next/dist/umd/i18next.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2642
node_modules/astro-i18next/node_modules/i18next/i18next.js
generated
vendored
Normal file
2642
node_modules/astro-i18next/node_modules/i18next/i18next.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/astro-i18next/node_modules/i18next/i18next.min.js
generated
vendored
Normal file
1
node_modules/astro-i18next/node_modules/i18next/i18next.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1509
node_modules/astro-i18next/node_modules/i18next/index.d.ts
generated
vendored
Normal file
1509
node_modules/astro-i18next/node_modules/i18next/index.d.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5
node_modules/astro-i18next/node_modules/i18next/index.js
generated
vendored
Normal file
5
node_modules/astro-i18next/node_modules/i18next/index.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/* eslint no-var: 0 */
|
||||
var main = require('./dist/cjs/i18next.js');
|
||||
|
||||
module.exports = main;
|
||||
module.exports.default = main;
|
129
node_modules/astro-i18next/node_modules/i18next/package.json
generated
vendored
Normal file
129
node_modules/astro-i18next/node_modules/i18next/package.json
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
{
|
||||
"name": "i18next",
|
||||
"version": "22.5.1",
|
||||
"description": "i18next internationalization framework",
|
||||
"main": "./dist/cjs/i18next.js",
|
||||
"module": "./dist/esm/i18next.js",
|
||||
"types": "./index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"types": "./index.d.ts",
|
||||
"import": "./dist/esm/i18next.js",
|
||||
"require": "./dist/cjs/i18next.js"
|
||||
}
|
||||
},
|
||||
"keywords": [
|
||||
"i18next",
|
||||
"internationalization",
|
||||
"i18n",
|
||||
"translation",
|
||||
"localization",
|
||||
"l10n",
|
||||
"globalization",
|
||||
"gettext"
|
||||
],
|
||||
"homepage": "https://www.i18next.com",
|
||||
"bugs": "https://github.com/i18next/i18next/issues",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/i18next/i18next.git"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://locize.com"
|
||||
},
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://locize.com/i18next.html"
|
||||
},
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.5",
|
||||
"@babel/plugin-proposal-async-generator-functions": "^7.20.1",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.19.6",
|
||||
"@babel/plugin-transform-runtime": "^7.19.6",
|
||||
"@babel/polyfill": "^7.2.5",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@babel/register": "^7.18.9",
|
||||
"@rollup/plugin-babel": "^5.2.2",
|
||||
"@rollup/plugin-commonjs": "^17.0.0",
|
||||
"@rollup/plugin-node-resolve": "^11.0.0",
|
||||
"@babel/eslint-parser": "^7.19.1",
|
||||
"babelify": "^10.0.0",
|
||||
"browserify": "17.0.0",
|
||||
"browserify-istanbul": "3.0.1",
|
||||
"chai": "4.3.7",
|
||||
"coveralls": "3.1.1",
|
||||
"cpy-cli": "^4.2.0",
|
||||
"dtslint": "^0.4.2",
|
||||
"eslint": "8.32.0",
|
||||
"eslint-config-airbnb": "19.0.4",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-import": "2.27.5",
|
||||
"eslint-plugin-jsx-a11y": "6.7.1",
|
||||
"eslint-plugin-react": "7.32.1",
|
||||
"gh-release": "6.0.4",
|
||||
"husky": "^7.0.2",
|
||||
"i18next-browser-languagedetector": "7.0.1",
|
||||
"i18next-fs-backend": "2.1.0",
|
||||
"i18next-http-backend": "2.1.0",
|
||||
"i18next-localstorage-cache": "1.1.1",
|
||||
"i18next-sprintf-postprocessor": "0.2.2",
|
||||
"karma": "6.4.1",
|
||||
"karma-browserify": "8.1.0",
|
||||
"karma-chai": "0.1.0",
|
||||
"karma-chrome-launcher": "3.1.1",
|
||||
"karma-cli": "2.0.0",
|
||||
"karma-coverage": "2.2.0",
|
||||
"karma-coveralls": "2.1.0",
|
||||
"karma-mocha": "2.0.1",
|
||||
"karma-rollup-preprocessor": "7.0.8",
|
||||
"karma-sinon": "1.0.5",
|
||||
"karma-spec-reporter": "0.0.34",
|
||||
"lint-staged": "^11.1.2",
|
||||
"mocha": "10.1.0",
|
||||
"nyc": "^15.1.0",
|
||||
"prettier": "^2.8.0",
|
||||
"rimraf": "3.0.2",
|
||||
"rollup": "^2.79.1",
|
||||
"rollup-plugin-terser": "^5.3.0",
|
||||
"sinon": "11.1.2",
|
||||
"tslint": "^5.20.1",
|
||||
"typescript": "^4.9.3",
|
||||
"watchify": "4.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"pretest": "npm run test:typescript && npm run test:typescript:customtypes && npm run test:typescript:noninterop",
|
||||
"lint": "eslint src",
|
||||
"test": "npm run lint && npm run test:new && npm run test:compat",
|
||||
"test:new": "karma start karma.conf.js --singleRun",
|
||||
"test:compat": "karma start karma.backward.conf.js --singleRun",
|
||||
"test:typescript": "tslint --project tsconfig.json",
|
||||
"test:typescript:customtypes": "tslint --project test/typescript/custom-types/tsconfig.json",
|
||||
"test:typescript:noninterop": "tslint --project tsconfig.nonEsModuleInterop.json",
|
||||
"tdd": "karma start karma.conf.js",
|
||||
"tdd:compat": "karma start karma.backward.conf.js",
|
||||
"build": "rimraf dist && rollup -c && echo '{\"type\":\"module\"}' > dist/esm/package.json && cpy \"./dist/umd/*.js\" ./",
|
||||
"fix_dist_package": "node -e 'console.log(`{\"type\":\"module\",\"version\":\"${process.env.npm_package_version}\"}`)' > dist/esm/package.json",
|
||||
"preversion": "npm run test && npm run build && git push",
|
||||
"postversion": "npm run fix_dist_package && git push && git push --tags && npm run release",
|
||||
"prettier": "prettier --write \"{,**/}*.{ts,tsx,js,json,md}\"",
|
||||
"prepare": "husky install",
|
||||
"release": "gh-release"
|
||||
},
|
||||
"author": "Jan Mühlemann <jan.muehlemann@gmail.com> (https://github.com/jamuhl)",
|
||||
"license": "MIT",
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx,js,json,md}": "prettier --write"
|
||||
}
|
||||
}
|
130
node_modules/astro-i18next/package.json
generated
vendored
Normal file
130
node_modules/astro-i18next/package.json
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
{
|
||||
"name": "astro-i18next",
|
||||
"version": "1.0.0-beta.21",
|
||||
"description": "An astro integration of i18next + some utility components to help you translate your astro websites!",
|
||||
"scripts": {
|
||||
"test": "vitest",
|
||||
"test:coverage": "pnpm test -- --coverage",
|
||||
"preview": "astro preview",
|
||||
"build": "./build.cjs && pnpm run typecheck:emit",
|
||||
"pack": "pnpm run build && pnpm pack",
|
||||
"lint": "eslint --ext js,cjs,ts .",
|
||||
"prettier": "prettier --check --ignore-path .prettierignore .",
|
||||
"prettier:fix": "prettier --write --ignore-path .prettierignore .",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"typecheck:emit": "tsc --declaration --emitDeclarationOnly --outDir dist/types",
|
||||
"commit": "cz",
|
||||
"prepare": "is-ci || husky install",
|
||||
"semantic-release": "semantic-release"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/yassinedoghri/astro-i18next.git"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"!src/index.d.ts",
|
||||
"!src/__tests__",
|
||||
"dist"
|
||||
],
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/types/index.d.ts",
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./components": "./src/components/index.ts"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"index": [
|
||||
"./dist/types/index.d.ts"
|
||||
],
|
||||
"components": [
|
||||
"./src/components/index.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"bin": {
|
||||
"astro-i18next": "./dist/cli/index.js"
|
||||
},
|
||||
"keywords": [
|
||||
"astro-component",
|
||||
"seo",
|
||||
"i18next",
|
||||
"i18n",
|
||||
"internationalization",
|
||||
"i10n",
|
||||
"localization"
|
||||
],
|
||||
"author": {
|
||||
"name": "Yassine Doghri",
|
||||
"email": "yassine@doghri.fr",
|
||||
"url": "https://yassinedoghri.com/"
|
||||
},
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/yassinedoghri/astro-i18next/issues"
|
||||
},
|
||||
"homepage": "https://astro-i18next.yassinedoghri.com/",
|
||||
"peerDependencies": {
|
||||
"astro": ">=1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@proload/core": "^0.3.3",
|
||||
"@proload/plugin-tsm": "^0.2.1",
|
||||
"i18next": "^22.4.10",
|
||||
"i18next-browser-languagedetector": "^7.0.1",
|
||||
"i18next-fs-backend": "^2.1.1",
|
||||
"i18next-http-backend": "^2.1.1",
|
||||
"iso-639-1": "^2.1.15",
|
||||
"locale-emoji": "^0.3.0",
|
||||
"pathe": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.4.4",
|
||||
"@commitlint/config-conventional": "^17.4.4",
|
||||
"@semantic-release/changelog": "^6.0.2",
|
||||
"@semantic-release/exec": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"@types/fs-extra": "^11.0.1",
|
||||
"@types/yargs": "^17.0.22",
|
||||
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
||||
"@typescript-eslint/parser": "^5.54.0",
|
||||
"@vitest/coverage-c8": "^0.29.2",
|
||||
"all-contributors-cli": "^6.24.0",
|
||||
"astro": "2.0.17",
|
||||
"commitizen": "^4.3.0",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"esbuild-plugin-fileloc": "^0.0.6",
|
||||
"esbuild": "^0.17.11",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-config-standard": "^17.0.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-jest": "^27.2.1",
|
||||
"eslint-plugin-n": "^15.6.1",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint": "^8.35.0",
|
||||
"fdir": "^6.0.1",
|
||||
"fs-extra": "^11.1.0",
|
||||
"husky": "^8.0.3",
|
||||
"i18next": "^22.4.10",
|
||||
"is-ci": "^3.0.1",
|
||||
"lint-staged": "^13.1.2",
|
||||
"prettier-plugin-astro": "^0.8.0",
|
||||
"prettier": "2.8.4",
|
||||
"semantic-release": "^20.1.1",
|
||||
"typescript": "^4.9.5",
|
||||
"vitest": "^0.29.2",
|
||||
"yargs": "^17.7.1"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,cjs,ts}": "eslint --ext js,cjs,ts . --cache --fix",
|
||||
"*.{js,cjs,ts,astro,css,md}": "prettier --write --ignore-path .prettierignore ."
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
}
|
||||
}
|
94
node_modules/astro-i18next/src/cli/generate.ts
generated
vendored
Normal file
94
node_modules/astro-i18next/src/cli/generate.ts
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
import fs from "fs";
|
||||
import { resolve } from "pathe";
|
||||
import { AstroI18nextConfig } from "../types";
|
||||
import {
|
||||
getAstroPagesFullPaths,
|
||||
createFiles,
|
||||
FileToGenerate,
|
||||
generateLocalizedFrontmatter,
|
||||
overwriteAstroFrontmatter,
|
||||
parseFrontmatter,
|
||||
resolveTranslatedAstroPath,
|
||||
resolveRelativePathsLevel,
|
||||
} from "./utils";
|
||||
|
||||
/**
|
||||
* Reads all files inside inputPath
|
||||
*
|
||||
* @param inputPath
|
||||
* @param locales
|
||||
* @param outputPath
|
||||
*/
|
||||
export const generate = (
|
||||
inputPath: string,
|
||||
defaultLocale: AstroI18nextConfig["defaultLocale"],
|
||||
locales: AstroI18nextConfig["locales"],
|
||||
showDefaultLocale = false,
|
||||
flatRoutes?: AstroI18nextConfig["flatRoutes"],
|
||||
outputPath: string = inputPath
|
||||
): { filesToGenerate: FileToGenerate[]; timeToProcess: number } => {
|
||||
const start = process.hrtime();
|
||||
|
||||
// default locale page paths
|
||||
const astroPagesFullPaths = showDefaultLocale
|
||||
? getAstroPagesFullPaths(inputPath, defaultLocale, locales)
|
||||
: getAstroPagesFullPaths(inputPath, undefined, locales);
|
||||
|
||||
const filesToGenerate: FileToGenerate[] = [];
|
||||
astroPagesFullPaths.forEach(async function (astroFileFullPath: string) {
|
||||
const astroFilePath = resolve(astroFileFullPath).replace(
|
||||
resolve(inputPath),
|
||||
""
|
||||
);
|
||||
|
||||
const inputFilePath = showDefaultLocale
|
||||
? [inputPath, defaultLocale, astroFilePath].join("/")
|
||||
: [inputPath, astroFilePath].join("/");
|
||||
|
||||
const fileContents = fs.readFileSync(inputFilePath);
|
||||
const fileContentsString = fileContents.toString();
|
||||
|
||||
const parsedFrontmatter = parseFrontmatter(fileContentsString);
|
||||
|
||||
locales.forEach((locale) => {
|
||||
const isOtherLocale = locale !== defaultLocale;
|
||||
const fileDepth = showDefaultLocale ? 0 : Number(isOtherLocale);
|
||||
|
||||
// add i18next's changeLanguage function to frontmatter
|
||||
const frontmatterCode = generateLocalizedFrontmatter(
|
||||
parsedFrontmatter,
|
||||
locale
|
||||
);
|
||||
|
||||
// get the astro file contents
|
||||
let newFileContents = overwriteAstroFrontmatter(
|
||||
fileContentsString,
|
||||
frontmatterCode
|
||||
);
|
||||
|
||||
// add depth to imports and Astro.glob pattern
|
||||
newFileContents = resolveRelativePathsLevel(newFileContents, fileDepth);
|
||||
|
||||
const createLocaleFolder = showDefaultLocale ? true : isOtherLocale;
|
||||
|
||||
filesToGenerate.push({
|
||||
path: resolve(
|
||||
resolveTranslatedAstroPath(
|
||||
astroFilePath,
|
||||
createLocaleFolder ? locale : undefined,
|
||||
outputPath,
|
||||
flatRoutes
|
||||
)
|
||||
),
|
||||
source: newFileContents,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
createFiles(filesToGenerate);
|
||||
|
||||
return {
|
||||
filesToGenerate,
|
||||
timeToProcess: process.hrtime(start)[1] / 1000000,
|
||||
};
|
||||
};
|
74
node_modules/astro-i18next/src/cli/index.ts
generated
vendored
Executable file
74
node_modules/astro-i18next/src/cli/index.ts
generated
vendored
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { flattenRoutes } from "../config";
|
||||
import yargs from "yargs";
|
||||
import { hideBin } from "yargs/helpers";
|
||||
import { generate } from "./generate";
|
||||
import { loadConfig, normalizePath } from "./middlewares";
|
||||
import { GenerateArgs } from "./types";
|
||||
|
||||
yargs(hideBin(process.argv))
|
||||
.usage("usage: $0 <command>")
|
||||
.command<GenerateArgs>(
|
||||
"generate [path] [options]",
|
||||
"generates localized Astro pages",
|
||||
(yargs) => {
|
||||
return yargs
|
||||
.positional("path", {
|
||||
type: "string",
|
||||
description: "Path to the Astro project folder",
|
||||
default: "./",
|
||||
})
|
||||
.option("output", {
|
||||
alias: "o",
|
||||
type: "string",
|
||||
description:
|
||||
"Set the output of the generated pages if different from input",
|
||||
});
|
||||
},
|
||||
async (argv) => {
|
||||
if (argv.verbose) {
|
||||
console.info(`Generating localized pages: ${argv.config.locales}`);
|
||||
}
|
||||
|
||||
const pagesPath = argv.path + "src/pages";
|
||||
|
||||
const flatRoutes = flattenRoutes(argv.config.routes);
|
||||
|
||||
const result = generate(
|
||||
pagesPath,
|
||||
argv.config.defaultLocale,
|
||||
argv.config.locales,
|
||||
argv.config.showDefaultLocale,
|
||||
flatRoutes,
|
||||
argv.output
|
||||
);
|
||||
|
||||
if (argv.verbose) {
|
||||
const filepaths = result.filesToGenerate.map(
|
||||
(fileToGenerate) => fileToGenerate.path
|
||||
);
|
||||
console.log(`\n✨ ${filepaths.join("\n✨ ")}\n`);
|
||||
}
|
||||
|
||||
// All good! Show success feedback
|
||||
console.log(
|
||||
`🧪 Localized .astro pages were generated successfully, it took ${result.timeToProcess.toFixed()}ms!`
|
||||
);
|
||||
}
|
||||
)
|
||||
.middleware([normalizePath, loadConfig], true)
|
||||
.options({
|
||||
config: {
|
||||
alias: "c",
|
||||
type: "string",
|
||||
description:
|
||||
"Set the output of the generated pages if different from input",
|
||||
},
|
||||
verbose: {
|
||||
alias: "v",
|
||||
type: "boolean",
|
||||
description: "Run with verbose logging",
|
||||
},
|
||||
})
|
||||
.parse();
|
32
node_modules/astro-i18next/src/cli/middlewares.ts
generated
vendored
Normal file
32
node_modules/astro-i18next/src/cli/middlewares.ts
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
import { pathToFileURL } from "url";
|
||||
import { MiddlewareFunction } from "yargs";
|
||||
import { getUserConfig } from "../utils";
|
||||
import { GenerateArgs, GlobalArgs } from "./types";
|
||||
|
||||
// @ts-ignore
|
||||
export const loadConfig: MiddlewareFunction<GlobalArgs & GenerateArgs> = async (
|
||||
argv
|
||||
): Promise<GlobalArgs & GenerateArgs> => {
|
||||
const { path, config } = argv;
|
||||
|
||||
const userConfig = await getUserConfig(pathToFileURL(path), config as any);
|
||||
|
||||
if (path && !userConfig?.value) {
|
||||
throw new Error(
|
||||
`Could not find a config file at ${JSON.stringify(
|
||||
path
|
||||
)}. Does the file exist?`
|
||||
);
|
||||
}
|
||||
|
||||
return { ...argv, config: userConfig?.value };
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
export const normalizePath: MiddlewareFunction<
|
||||
GlobalArgs & GenerateArgs
|
||||
> = async (argv): Promise<GlobalArgs & GenerateArgs> => {
|
||||
const { path } = argv;
|
||||
|
||||
return { ...argv, path: path.endsWith("/") ? path : path + "/" };
|
||||
};
|
162
node_modules/astro-i18next/src/cli/transformer.ts
generated
vendored
Normal file
162
node_modules/astro-i18next/src/cli/transformer.ts
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
import ts from "typescript";
|
||||
|
||||
/**
|
||||
* Traverse ts' AST to inject i18next's language switch
|
||||
* @param context
|
||||
* @returns
|
||||
*/
|
||||
export const transformer: ts.TransformerFactory<ts.SourceFile> =
|
||||
(context: ts.TransformationContext) => (rootNode) => {
|
||||
const { factory, getCompilerOptions } = context;
|
||||
let doesI18nextImportExist = false;
|
||||
|
||||
const { locale } = getCompilerOptions();
|
||||
|
||||
function visit(node: ts.Node): ts.Node {
|
||||
// isolate i18next import statement
|
||||
if (
|
||||
ts.isImportDeclaration(node) &&
|
||||
ts.isStringLiteral(node.moduleSpecifier) &&
|
||||
node.moduleSpecifier.text === "i18next" &&
|
||||
ts.isImportClause(node.importClause)
|
||||
) {
|
||||
doesI18nextImportExist = true;
|
||||
|
||||
if (!node.importClause.namedBindings) {
|
||||
return factory.updateImportDeclaration(
|
||||
node,
|
||||
node.modifiers,
|
||||
factory.createImportClause(
|
||||
node.importClause.isTypeOnly,
|
||||
node.importClause.name,
|
||||
factory.createNamedImports([
|
||||
factory.createImportSpecifier(
|
||||
false,
|
||||
undefined,
|
||||
factory.createIdentifier("changeLanguage")
|
||||
),
|
||||
])
|
||||
),
|
||||
node.moduleSpecifier,
|
||||
node.assertClause
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
ts.isImportClause(node.importClause) &&
|
||||
ts.isNamedImports(node.importClause.namedBindings) &&
|
||||
!node.importClause.namedBindings.elements.find(
|
||||
(namedImport) => namedImport.name.escapedText === "changeLanguage"
|
||||
)
|
||||
) {
|
||||
// import changeLanguage function in i18next import declaration
|
||||
return factory.updateImportDeclaration(
|
||||
node,
|
||||
node.modifiers,
|
||||
factory.updateImportClause(
|
||||
node.importClause,
|
||||
false,
|
||||
node.importClause.name,
|
||||
factory.updateNamedImports(node.importClause.namedBindings, [
|
||||
...node.importClause.namedBindings.elements,
|
||||
factory.createImportSpecifier(
|
||||
false,
|
||||
undefined,
|
||||
factory.createIdentifier("changeLanguage")
|
||||
),
|
||||
])
|
||||
),
|
||||
node.moduleSpecifier,
|
||||
node.assertClause
|
||||
);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
// remove any occurrence of changeLanguage() call
|
||||
if (
|
||||
ts.isExpressionStatement(node) &&
|
||||
ts.isCallExpression(node.expression) &&
|
||||
((ts.isPropertyAccessExpression(node.expression.expression) &&
|
||||
node.expression.expression.name.escapedText === "changeLanguage") ||
|
||||
(ts.isIdentifier(node.expression.expression) &&
|
||||
node.expression.expression.escapedText === "changeLanguage"))
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return ts.visitEachChild(node, visit, context);
|
||||
}
|
||||
|
||||
let visitedNode = ts.visitNode(rootNode, visit);
|
||||
|
||||
const statements = [...visitedNode.statements];
|
||||
|
||||
if (!doesI18nextImportExist) {
|
||||
statements.unshift(
|
||||
factory.createImportDeclaration(
|
||||
undefined,
|
||||
factory.createImportClause(
|
||||
false,
|
||||
undefined,
|
||||
factory.createNamedImports([
|
||||
factory.createImportSpecifier(
|
||||
false,
|
||||
undefined,
|
||||
factory.createIdentifier("changeLanguage")
|
||||
),
|
||||
])
|
||||
),
|
||||
factory.createStringLiteral("i18next"),
|
||||
undefined
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// append the changeLanguage statement after imports
|
||||
|
||||
// get a boolean array with values telling whether or not a statement is an import
|
||||
const importDeclarationsMap = statements.map((statement) =>
|
||||
ts.isImportDeclaration(statement)
|
||||
);
|
||||
|
||||
const lastIndexOfImportDeclaration =
|
||||
importDeclarationsMap.lastIndexOf(true);
|
||||
|
||||
// insert changeLanguage statement after the imports
|
||||
// and surrounded by line breaks
|
||||
statements.splice(
|
||||
lastIndexOfImportDeclaration + 1,
|
||||
0,
|
||||
factory.createIdentifier("\n") as unknown as ts.Statement
|
||||
);
|
||||
statements.splice(
|
||||
lastIndexOfImportDeclaration + 1,
|
||||
0,
|
||||
factory.createExpressionStatement(
|
||||
factory.createCallExpression(
|
||||
factory.createIdentifier("changeLanguage"),
|
||||
undefined,
|
||||
[factory.createStringLiteral(locale as string)]
|
||||
)
|
||||
)
|
||||
);
|
||||
statements.splice(
|
||||
lastIndexOfImportDeclaration + 1,
|
||||
0,
|
||||
factory.createIdentifier("\n") as unknown as ts.Statement
|
||||
);
|
||||
|
||||
visitedNode = factory.updateSourceFile(
|
||||
visitedNode,
|
||||
statements,
|
||||
visitedNode.isDeclarationFile,
|
||||
visitedNode.referencedFiles,
|
||||
visitedNode.typeReferenceDirectives,
|
||||
visitedNode.hasNoDefaultLib,
|
||||
visitedNode.libReferenceDirectives
|
||||
);
|
||||
|
||||
return visitedNode;
|
||||
};
|
11
node_modules/astro-i18next/src/cli/types.ts
generated
vendored
Normal file
11
node_modules/astro-i18next/src/cli/types.ts
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { AstroI18nextConfig } from "../types";
|
||||
|
||||
export interface GlobalArgs {
|
||||
verbose: boolean;
|
||||
}
|
||||
|
||||
export interface GenerateArgs {
|
||||
path: string;
|
||||
config: AstroI18nextConfig;
|
||||
output: string;
|
||||
}
|
182
node_modules/astro-i18next/src/cli/utils.ts
generated
vendored
Normal file
182
node_modules/astro-i18next/src/cli/utils.ts
generated
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
import { fdir, PathsOutput } from "fdir";
|
||||
import fsExtra from "fs-extra";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import ts from "typescript";
|
||||
import { transformer } from "./transformer";
|
||||
import { AstroI18nextConfig } from "../types";
|
||||
|
||||
export interface FileToGenerate {
|
||||
path: string;
|
||||
source: string;
|
||||
}
|
||||
|
||||
export const doesStringIncludeFrontmatter = (source: string): boolean =>
|
||||
/---.*---/s.test(source);
|
||||
|
||||
export const extractFrontmatterFromAstroSource = (source: string): string => {
|
||||
if (doesStringIncludeFrontmatter(source)) {
|
||||
const {
|
||||
groups: { frontmatter },
|
||||
} = /---(?<frontmatter>(.*))---/s.exec(source);
|
||||
|
||||
return frontmatter;
|
||||
}
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
export const overwriteAstroFrontmatter = (
|
||||
source: string,
|
||||
frontmatter: string
|
||||
): string => {
|
||||
if (doesStringIncludeFrontmatter(source)) {
|
||||
return source.replace(/---[\s\S]*---/g, `---\n${frontmatter.trim()}\n---`);
|
||||
}
|
||||
|
||||
return `---\n${frontmatter.trim()}\n---\n\n` + source;
|
||||
};
|
||||
|
||||
export const addDepthToRelativePath = (
|
||||
relativePath: string,
|
||||
depth: number = 1
|
||||
): string => {
|
||||
if (relativePath.startsWith("./") && depth > 0) {
|
||||
// remove "./" from relativePath
|
||||
relativePath = relativePath.slice(2);
|
||||
}
|
||||
|
||||
return relativePath.padStart(relativePath.length + depth * 3, "../");
|
||||
};
|
||||
|
||||
/**
|
||||
* file is hidden if its name or any of its parent folders start with an underscore
|
||||
*/
|
||||
export const isFileHidden = (filepath: string): boolean => {
|
||||
return /((^_)|(\/_))/.test(filepath);
|
||||
};
|
||||
|
||||
export const resolveRelativePathsLevel = (
|
||||
fileContents: string,
|
||||
fileDepth: number
|
||||
) => {
|
||||
fileContents = fileContents.replace(
|
||||
/(import\s+.*["'])(\..*)(["'])/g,
|
||||
(_, before, relativePath, after) =>
|
||||
`${before}${addDepthToRelativePath(relativePath, fileDepth)}${after}`
|
||||
);
|
||||
fileContents = fileContents.replace(
|
||||
/(Astro.glob\(["'])(\..*)(["']\))/g,
|
||||
(_, before, relativePath, after) =>
|
||||
`${before}${addDepthToRelativePath(relativePath, fileDepth)}${after}`
|
||||
);
|
||||
fileContents = fileContents.replace(
|
||||
/(<script\s+src=["'])(\..*)(["'])/g,
|
||||
(_, before, relativePath, after) =>
|
||||
`${before}${addDepthToRelativePath(relativePath, fileDepth)}${after}`
|
||||
);
|
||||
|
||||
return fileContents;
|
||||
};
|
||||
|
||||
/* c8 ignore start */
|
||||
/**
|
||||
* parse frontmatter using typescript compiler
|
||||
*
|
||||
* @param source
|
||||
*/
|
||||
export const parseFrontmatter = (source: string): ts.SourceFile =>
|
||||
ts.createSourceFile(
|
||||
"x.ts",
|
||||
extractFrontmatterFromAstroSource(source),
|
||||
ts.ScriptTarget.Latest
|
||||
);
|
||||
|
||||
export const generateLocalizedFrontmatter = (
|
||||
tsNode: ts.SourceFile,
|
||||
locale: string
|
||||
) => {
|
||||
// generate for default locale, then loop over locales to generate other pages
|
||||
const result: ts.TransformationResult<ts.SourceFile> = ts.transform(
|
||||
tsNode,
|
||||
[transformer],
|
||||
{ locale }
|
||||
);
|
||||
const printer = ts.createPrinter();
|
||||
|
||||
return printer.printNode(
|
||||
ts.EmitHint.Unspecified,
|
||||
result.transformed[0],
|
||||
tsNode
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Crawls pages directory and returns all Astro pages
|
||||
* except for locale folders and excluded pages / directories (starting with underscore).
|
||||
* (https://docs.astro.build/en/core-concepts/routing/#excluding-pages)
|
||||
*
|
||||
* @param pagesDirectoryPath
|
||||
* @param childDirToCrawl will make the function crawl inside the given
|
||||
* `childDirToCrawl` (doesn't take paths, only dirname).
|
||||
*/
|
||||
export const getAstroPagesFullPaths = (
|
||||
pagesDirectoryPath: string,
|
||||
childDirToCrawl: AstroI18nextConfig["defaultLocale"] | undefined = undefined,
|
||||
locales: AstroI18nextConfig["locales"] = []
|
||||
): PathsOutput => {
|
||||
// eslint-disable-next-line new-cap
|
||||
const api = new fdir()
|
||||
.filter(
|
||||
(filepath) => !isFileHidden(filepath) && filepath.endsWith(".astro")
|
||||
)
|
||||
.exclude((dirName) => locales.includes(dirName))
|
||||
.withFullPaths();
|
||||
|
||||
return childDirToCrawl
|
||||
? (api
|
||||
.crawl(`${pagesDirectoryPath}${path.sep}${childDirToCrawl}`)
|
||||
.sync() as PathsOutput)
|
||||
: (api.crawl(pagesDirectoryPath).sync() as PathsOutput);
|
||||
};
|
||||
|
||||
export const createFiles = (filesToGenerate: FileToGenerate[]): void => {
|
||||
filesToGenerate.forEach((fileToGenerate) => {
|
||||
fsExtra.ensureDirSync(path.dirname(fileToGenerate.path));
|
||||
|
||||
fs.writeFileSync(fileToGenerate.path, fileToGenerate.source);
|
||||
});
|
||||
};
|
||||
/* c8 ignore stop */
|
||||
|
||||
/**
|
||||
* Resolves the right translated path based on
|
||||
* a given `astroFilePath` and a locale,
|
||||
* with the `routeTranslations` mapping.
|
||||
*/
|
||||
export const resolveTranslatedAstroPath = (
|
||||
astroFilePath: string,
|
||||
locale: string | null = null,
|
||||
basePath: string = "",
|
||||
flatRoutes: AstroI18nextConfig["flatRoutes"] = {}
|
||||
) => {
|
||||
astroFilePath = astroFilePath.replace(/^\/+|\/+$/g, "");
|
||||
|
||||
// remove trailing slash of basePath if any
|
||||
basePath = basePath.replace(/\/+$/g, "");
|
||||
|
||||
if (locale === null) {
|
||||
return `${basePath}/${astroFilePath}`;
|
||||
}
|
||||
|
||||
astroFilePath = astroFilePath.replace(/.astro$/, "");
|
||||
|
||||
const filePath = `/${locale}/${astroFilePath}`;
|
||||
|
||||
// is route translated?
|
||||
if (Object.prototype.hasOwnProperty.call(flatRoutes, filePath)) {
|
||||
return `${basePath}${flatRoutes[filePath]}.astro`;
|
||||
}
|
||||
|
||||
return `${basePath}/${locale}/${astroFilePath}.astro`;
|
||||
};
|
17
node_modules/astro-i18next/src/components/HeadHrefLangs.astro
generated
vendored
Normal file
17
node_modules/astro-i18next/src/components/HeadHrefLangs.astro
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
import i18next from "i18next";
|
||||
import { localizeUrl } from "../..";
|
||||
|
||||
const supportedLanguages = i18next.languages;
|
||||
const currentUrl = Astro.url.href;
|
||||
---
|
||||
|
||||
{
|
||||
supportedLanguages.map((supportedLanguage) => (
|
||||
<link
|
||||
rel="alternate"
|
||||
hreflang={supportedLanguage}
|
||||
href={localizeUrl(currentUrl, supportedLanguage)}
|
||||
/>
|
||||
))
|
||||
}
|
49
node_modules/astro-i18next/src/components/LanguageSelector.astro
generated
vendored
Normal file
49
node_modules/astro-i18next/src/components/LanguageSelector.astro
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
import i18next from "i18next";
|
||||
import { localizePath } from "../..";
|
||||
import localeEmoji from "locale-emoji";
|
||||
import ISO6991 from "iso-639-1";
|
||||
|
||||
interface languageMapping {
|
||||
[localeCode: string]: string;
|
||||
}
|
||||
|
||||
export interface Props extends astroHTML.JSX.SelectHTMLAttributes {
|
||||
showFlag?: boolean;
|
||||
languageMapping?: languageMapping;
|
||||
}
|
||||
|
||||
const supportedLanguages = i18next.languages;
|
||||
const currentLanguage = i18next.language;
|
||||
|
||||
const { pathname } = Astro.url;
|
||||
|
||||
const { showFlag = false, languageMapping, ...attributes } = Astro.props;
|
||||
---
|
||||
|
||||
<select onchange="location = this.value;" {...attributes}>
|
||||
{
|
||||
supportedLanguages.map((supportedLanguage: string) => {
|
||||
let value = localizePath(pathname, supportedLanguage);
|
||||
const flag = showFlag ? localeEmoji(supportedLanguage) + " " : "";
|
||||
|
||||
let nativeName = "";
|
||||
if (
|
||||
languageMapping &&
|
||||
languageMapping.hasOwnProperty(supportedLanguage)
|
||||
) {
|
||||
nativeName = languageMapping[supportedLanguage];
|
||||
} else {
|
||||
nativeName = ISO6991.getNativeName(supportedLanguage);
|
||||
}
|
||||
|
||||
const label = flag + nativeName;
|
||||
|
||||
return (
|
||||
<option value={value} selected={supportedLanguage === currentLanguage}>
|
||||
{label}
|
||||
</option>
|
||||
);
|
||||
})
|
||||
}
|
||||
</select>
|
21
node_modules/astro-i18next/src/components/Trans.astro
generated
vendored
Normal file
21
node_modules/astro-i18next/src/components/Trans.astro
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
import { interpolate, createReferenceStringFromHTML } from "..";
|
||||
|
||||
export interface Props {
|
||||
i18nKey?: string;
|
||||
ns?: string;
|
||||
}
|
||||
|
||||
const { i18nKey, ns } = Astro.props;
|
||||
|
||||
const referenceString = await Astro.slots.render("default");
|
||||
|
||||
let key: string;
|
||||
if (typeof i18nKey === "string") {
|
||||
key = i18nKey;
|
||||
} else {
|
||||
key = createReferenceStringFromHTML(referenceString);
|
||||
}
|
||||
---
|
||||
|
||||
<Fragment set:html={interpolate(key, referenceString, ns)} />
|
3
node_modules/astro-i18next/src/components/index.d.ts
generated
vendored
Normal file
3
node_modules/astro-i18next/src/components/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as Trans } from "./Trans.astro";
|
||||
export { default as LanguageSelector } from "./LanguageSelector.astro";
|
||||
export { default as HeadHrefLangs } from "./HeadHrefLangs.astro";
|
3
node_modules/astro-i18next/src/components/index.ts
generated
vendored
Normal file
3
node_modules/astro-i18next/src/components/index.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as Trans } from "./Trans.astro";
|
||||
export { default as LanguageSelector } from "./LanguageSelector.astro";
|
||||
export { default as HeadHrefLangs } from "./HeadHrefLangs.astro";
|
89
node_modules/astro-i18next/src/config.ts
generated
vendored
Normal file
89
node_modules/astro-i18next/src/config.ts
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
import { AstroI18nextConfig, AstroI18nextGlobal, Routes } from "./types";
|
||||
|
||||
export const AstroI18next: AstroI18nextGlobal = {
|
||||
config: {
|
||||
defaultLocale: "cimode",
|
||||
locales: [],
|
||||
namespaces: "translation",
|
||||
defaultNamespace: "translation",
|
||||
load: ["server"],
|
||||
routes: {},
|
||||
flatRoutes: {},
|
||||
showDefaultLocale: false,
|
||||
trailingSlash: "ignore",
|
||||
resourcesBasePath: "/locales",
|
||||
},
|
||||
};
|
||||
|
||||
/* c8 ignore start */
|
||||
export const setAstroI18nextConfig = (config: AstroI18nextConfig) => {
|
||||
let flatRoutes = {};
|
||||
for (const key in config) {
|
||||
if (key === "routes") {
|
||||
flatRoutes = flattenRoutes(config[key]);
|
||||
}
|
||||
|
||||
AstroI18next.config[key] = config[key];
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
AstroI18next.config.flatRoutes = flatRoutes;
|
||||
};
|
||||
|
||||
export const astroI18nextConfigBuilder = (
|
||||
config: AstroI18nextConfig
|
||||
): AstroI18nextConfig => {
|
||||
return { ...AstroI18next.config, ...config };
|
||||
};
|
||||
/* c8 ignore stop */
|
||||
|
||||
/**
|
||||
* This will create a mapping of translated routes to search them easily.
|
||||
*
|
||||
* TODO: render all routes mappings in here (even those not translated),
|
||||
* this will help simplify utility functions logic
|
||||
*/
|
||||
export const flattenRoutes = (
|
||||
routes: AstroI18nextConfig["routes"],
|
||||
previous: string[] = [],
|
||||
translatedPrevious: string[] = [],
|
||||
prevResult: AstroI18nextConfig["flatRoutes"] = null
|
||||
): AstroI18nextConfig["flatRoutes"] => {
|
||||
let result = prevResult || {};
|
||||
|
||||
for (const i in routes) {
|
||||
if (typeof routes[i] === "object" && routes[i] !== null) {
|
||||
// Recursion on deeper objects
|
||||
flattenRoutes(
|
||||
routes[i] as Routes,
|
||||
[...previous, i],
|
||||
[
|
||||
...translatedPrevious,
|
||||
Object.prototype.hasOwnProperty.call(routes[i], "index")
|
||||
? routes[i]["index"]
|
||||
: i,
|
||||
],
|
||||
result
|
||||
);
|
||||
} else {
|
||||
let key = "/" + previous.join("/");
|
||||
let value = "/" + translatedPrevious.join("/");
|
||||
|
||||
if (i === "index") {
|
||||
result[key] = value;
|
||||
|
||||
key += "/" + i;
|
||||
value += "/" + i;
|
||||
|
||||
result[key] = value;
|
||||
} else {
|
||||
key += "/" + i;
|
||||
value += "/" + routes[i];
|
||||
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
185
node_modules/astro-i18next/src/index.ts
generated
vendored
Normal file
185
node_modules/astro-i18next/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
import { AstroIntegration } from "astro";
|
||||
import { InitOptions } from "i18next";
|
||||
import { astroI18nextConfigBuilder, setAstroI18nextConfig } from "./config";
|
||||
import { AstroI18nextConfig, AstroI18nextOptions, Plugins } from "./types";
|
||||
import {
|
||||
moveDefaultLocaleToFirstIndex,
|
||||
deeplyStringifyObject,
|
||||
getUserConfig,
|
||||
} from "./utils";
|
||||
import { resolve } from "pathe";
|
||||
|
||||
export default (options?: AstroI18nextOptions): AstroIntegration => {
|
||||
const customConfigPath = options?.configPath;
|
||||
|
||||
return {
|
||||
name: "astro-i18next",
|
||||
hooks: {
|
||||
"astro:config:setup": async ({ config, injectScript }) => {
|
||||
/**
|
||||
* 0. Get user config
|
||||
*/
|
||||
const userConfig = await getUserConfig(config.root, customConfigPath);
|
||||
|
||||
if (customConfigPath && !userConfig?.value) {
|
||||
throw new Error(
|
||||
`[astro-i18next]: Could not find a config file at ${JSON.stringify(
|
||||
customConfigPath
|
||||
)}. Does the file exist?`
|
||||
);
|
||||
}
|
||||
|
||||
const astroI18nextConfig: AstroI18nextConfig =
|
||||
astroI18nextConfigBuilder(userConfig?.value as AstroI18nextConfig);
|
||||
|
||||
/**
|
||||
* 1. Validate and prepare config
|
||||
*/
|
||||
if (
|
||||
!astroI18nextConfig.defaultLocale ||
|
||||
astroI18nextConfig.defaultLocale === ""
|
||||
) {
|
||||
throw new Error(
|
||||
"[astro-i18next]: you must set a `defaultLocale` in your astro-i18next config!"
|
||||
);
|
||||
}
|
||||
|
||||
if (!astroI18nextConfig.locales) {
|
||||
astroI18nextConfig.locales = [astroI18nextConfig.defaultLocale];
|
||||
}
|
||||
|
||||
if (
|
||||
!astroI18nextConfig.locales.includes(astroI18nextConfig.defaultLocale)
|
||||
) {
|
||||
astroI18nextConfig.locales.unshift(astroI18nextConfig.defaultLocale);
|
||||
}
|
||||
|
||||
// make sure to have default locale set as first element in locales (for supportedLngs)
|
||||
if (
|
||||
astroI18nextConfig.locales[0] !== astroI18nextConfig.defaultLocale
|
||||
) {
|
||||
moveDefaultLocaleToFirstIndex(
|
||||
astroI18nextConfig.locales as string[],
|
||||
astroI18nextConfig.defaultLocale
|
||||
);
|
||||
}
|
||||
|
||||
// add trailingSlash config from astro if not set
|
||||
astroI18nextConfig.trailingSlash = config.trailingSlash;
|
||||
|
||||
if (astroI18nextConfig.load.includes("server")) {
|
||||
// Build server side i18next config
|
||||
// set i18next supported and fallback languages (same as locales)
|
||||
const serverConfig: InitOptions = {
|
||||
supportedLngs: astroI18nextConfig.locales as string[],
|
||||
fallbackLng: astroI18nextConfig.locales as string[],
|
||||
ns: astroI18nextConfig.namespaces,
|
||||
defaultNS: astroI18nextConfig.defaultNamespace,
|
||||
initImmediate: false,
|
||||
backend: {
|
||||
loadPath: resolve(
|
||||
`${config.publicDir.pathname}/${astroI18nextConfig.resourcesBasePath}/{{lng}}/{{ns}}.json`
|
||||
),
|
||||
},
|
||||
...astroI18nextConfig.i18nextServer,
|
||||
};
|
||||
|
||||
const defaultI18nextServerPlugins: Plugins = {
|
||||
fsBackend: "i18next-fs-backend",
|
||||
};
|
||||
|
||||
const i18nextServerPlugins = {
|
||||
...defaultI18nextServerPlugins,
|
||||
...astroI18nextConfig.i18nextServerPlugins,
|
||||
};
|
||||
|
||||
let { imports: serverImports, i18nextInit: i18nextInitServer } =
|
||||
i18nextScriptBuilder(serverConfig, i18nextServerPlugins);
|
||||
|
||||
// initializing runtime astro-i18next config
|
||||
serverImports += `import {initAstroI18next} from "astro-i18next";`;
|
||||
const astroI18nextInit = `initAstroI18next(${deeplyStringifyObject(
|
||||
astroI18nextConfig
|
||||
)});`;
|
||||
|
||||
// server side i18next instance
|
||||
injectScript(
|
||||
"page-ssr",
|
||||
serverImports + i18nextInitServer + astroI18nextInit
|
||||
);
|
||||
}
|
||||
|
||||
if (astroI18nextConfig.load?.includes("client")) {
|
||||
const clientConfig: InitOptions = {
|
||||
supportedLngs: astroI18nextConfig.locales as string[],
|
||||
fallbackLng: astroI18nextConfig.locales as string[],
|
||||
ns: astroI18nextConfig.namespaces,
|
||||
defaultNS: astroI18nextConfig.defaultNamespace,
|
||||
detection: {
|
||||
order: ["htmlTag"],
|
||||
caches: [],
|
||||
},
|
||||
backend: {
|
||||
loadPath: `${astroI18nextConfig.resourcesBasePath}/{{lng}}/{{ns}}.json`,
|
||||
},
|
||||
...astroI18nextConfig.i18nextClient,
|
||||
};
|
||||
|
||||
const defaultI18nextClientPlugins: Plugins = {
|
||||
httpBackend: "i18next-http-backend",
|
||||
LanguageDetector: "i18next-browser-languagedetector",
|
||||
};
|
||||
|
||||
const i18nextClientPlugins = {
|
||||
...defaultI18nextClientPlugins,
|
||||
...astroI18nextConfig.i18nextClientPlugins,
|
||||
};
|
||||
|
||||
let { imports: clientImports, i18nextInit: i18nextInitClient } =
|
||||
i18nextScriptBuilder(clientConfig, i18nextClientPlugins);
|
||||
|
||||
// client side i18next instance
|
||||
injectScript("before-hydration", clientImports + i18nextInitClient);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const i18nextScriptBuilder = (config: InitOptions, plugins: Plugins) => {
|
||||
let imports = `import i18next from "i18next";`;
|
||||
let i18nextInit = "i18next";
|
||||
|
||||
if (Object.keys(plugins).length > 0) {
|
||||
for (const key of Object.keys(plugins)) {
|
||||
// discard plugin if it does not have import name
|
||||
if (plugins[key] === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
imports += `import ${key} from "${plugins[key]}";`;
|
||||
i18nextInit += `.use(${key.replace(/[{}]/g, "")})`;
|
||||
}
|
||||
}
|
||||
|
||||
i18nextInit += `.init(${deeplyStringifyObject(config)});`;
|
||||
|
||||
return { imports, i18nextInit };
|
||||
};
|
||||
|
||||
export function initAstroI18next(config: AstroI18nextConfig) {
|
||||
// init runtime config
|
||||
setAstroI18nextConfig(config);
|
||||
}
|
||||
|
||||
export { AstroI18next } from "./config";
|
||||
|
||||
export {
|
||||
createReferenceStringFromHTML,
|
||||
detectLocaleFromPath,
|
||||
interpolate,
|
||||
localizePath,
|
||||
localizeUrl,
|
||||
} from "./utils";
|
||||
|
||||
export { AstroI18nextConfig, AstroI18nextOptions } from "./types";
|
134
node_modules/astro-i18next/src/types.ts
generated
vendored
Normal file
134
node_modules/astro-i18next/src/types.ts
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
import { InitOptions } from "i18next";
|
||||
|
||||
export interface AstroI18nextGlobal {
|
||||
config: AstroI18nextConfig;
|
||||
}
|
||||
|
||||
export interface AstroI18nextOptions {
|
||||
/**
|
||||
* Path to your astro-i18next config file
|
||||
*
|
||||
* @default 'astro-i18next.config.js'
|
||||
*/
|
||||
configPath?: string;
|
||||
}
|
||||
|
||||
export interface Routes {
|
||||
[segment: string]: string | Record<string, string | Routes>;
|
||||
}
|
||||
|
||||
export interface Plugins {
|
||||
[importName: string]: string | null;
|
||||
}
|
||||
|
||||
export interface AstroI18nextConfig {
|
||||
/**
|
||||
* The default locale for your website.
|
||||
*
|
||||
* @default "cimode"
|
||||
*/
|
||||
defaultLocale: string;
|
||||
|
||||
/**
|
||||
* The locales that are supported by your website.
|
||||
*
|
||||
* @default []
|
||||
*/
|
||||
locales: string[];
|
||||
|
||||
/**
|
||||
* String or array of namespaces to load
|
||||
*
|
||||
* @default "translation"
|
||||
*/
|
||||
namespaces?: string | string[];
|
||||
|
||||
/**
|
||||
* Default namespace used if not passed to the translation function
|
||||
*
|
||||
* @default "translation"
|
||||
*/
|
||||
defaultNamespace?: string;
|
||||
|
||||
/**
|
||||
* Load i18next on server side only, client side only or both.
|
||||
*
|
||||
* @default ["server"]
|
||||
*/
|
||||
load?: ("server" | "client")[];
|
||||
|
||||
/**
|
||||
* Set base path for i18next resources.
|
||||
*
|
||||
* @default "/locales"
|
||||
*/
|
||||
resourcesBasePath?: string;
|
||||
|
||||
/**
|
||||
* i18next server side config. See https://www.i18next.com/overview/configuration-options
|
||||
*/
|
||||
i18nextServer?: InitOptions;
|
||||
|
||||
/**
|
||||
* i18next client side config. See https://www.i18next.com/overview/configuration-options
|
||||
*/
|
||||
i18nextClient?: InitOptions;
|
||||
|
||||
/**
|
||||
* The translations for your routes.
|
||||
*
|
||||
* @default {}
|
||||
*/
|
||||
routes?: Routes;
|
||||
|
||||
/**
|
||||
* Generated mappings based on the routes
|
||||
*
|
||||
* @default {}
|
||||
*/
|
||||
readonly flatRoutes?: Record<string, string>;
|
||||
|
||||
/**
|
||||
* The display behaviour for the URL locale.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
showDefaultLocale?: boolean;
|
||||
|
||||
/**
|
||||
* i18next server side plugins. See https://www.i18next.com/overview/plugins-and-utils
|
||||
*
|
||||
* Include the plugins with the key being the import name and the value being the plugin name.
|
||||
*
|
||||
* Eg.:
|
||||
* ```
|
||||
* {
|
||||
* "Backend": "i18next-fs-backend",
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
i18nextServerPlugins?: Plugins;
|
||||
|
||||
/**
|
||||
* i18next client side plugins. See https://www.i18next.com/overview/plugins-and-utils
|
||||
*
|
||||
* Include the plugins with the key being the import name and the value being the plugin name.
|
||||
*
|
||||
* Eg.:
|
||||
* ```
|
||||
* {
|
||||
* "{initReactI18next}": "react-i18next",
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
i18nextClientPlugins?: Plugins;
|
||||
|
||||
/**
|
||||
* Set the route matching behavior of the dev server. Choose from the following options:
|
||||
*
|
||||
* 'always' - Only match URLs that include a trailing slash (ex: "/foo/")
|
||||
* 'never' - Never match URLs that include a trailing slash (ex: "/foo")
|
||||
* 'ignore' - Match URLs regardless of whether a trailing "/" exists
|
||||
*/
|
||||
trailingSlash?: "always" | "never" | "ignore";
|
||||
}
|
387
node_modules/astro-i18next/src/utils.ts
generated
vendored
Normal file
387
node_modules/astro-i18next/src/utils.ts
generated
vendored
Normal file
@@ -0,0 +1,387 @@
|
||||
import i18next, { t } from "i18next";
|
||||
import { fileURLToPath } from "url";
|
||||
import load from "@proload/core";
|
||||
import { AstroI18nextConfig } from "./types";
|
||||
import typescript from "@proload/plugin-tsm";
|
||||
import { AstroI18next } from "./config";
|
||||
|
||||
/**
|
||||
* Adapted from astro's tailwind integration:
|
||||
* https://github.com/withastro/astro/tree/main/packages/integrations/tailwind
|
||||
*/
|
||||
/* c8 ignore start */
|
||||
export const getUserConfig = async (
|
||||
root: URL,
|
||||
configPath?: string
|
||||
): Promise<load.Config<AstroI18nextConfig>> => {
|
||||
const resolvedRoot = fileURLToPath(root);
|
||||
let userConfigPath: string | undefined;
|
||||
|
||||
if (configPath) {
|
||||
const configPathWithLeadingSlash = /^\.*\//.test(configPath)
|
||||
? configPath
|
||||
: `./${configPath}`;
|
||||
|
||||
userConfigPath = fileURLToPath(new URL(configPathWithLeadingSlash, root));
|
||||
}
|
||||
|
||||
load.use([typescript]);
|
||||
return (await load("astro-i18next", {
|
||||
mustExist: false,
|
||||
cwd: resolvedRoot,
|
||||
filePath: userConfigPath,
|
||||
})) as load.Config<AstroI18nextConfig>;
|
||||
};
|
||||
/* c8 ignore stop */
|
||||
|
||||
/**
|
||||
* Moves the default locale in the first index
|
||||
*/
|
||||
export const moveDefaultLocaleToFirstIndex = (
|
||||
locales: string[],
|
||||
baseLocale: string
|
||||
): void => {
|
||||
const baseLocaleIndex = locales.indexOf(baseLocale);
|
||||
locales.splice(baseLocaleIndex, 1);
|
||||
locales.unshift(baseLocale);
|
||||
};
|
||||
|
||||
/**
|
||||
* Interpolates a localized string (loaded with the i18nKey) to a given reference string.
|
||||
*/
|
||||
export const interpolate = (
|
||||
i18nKey: string,
|
||||
referenceString: string,
|
||||
namespace: string | null = null
|
||||
): string => {
|
||||
const localizedString = t(i18nKey, { ns: namespace });
|
||||
|
||||
if (localizedString === i18nKey) {
|
||||
console.warn(`WARNING(astro-i18next): missing translation key ${i18nKey}.`);
|
||||
return referenceString;
|
||||
}
|
||||
|
||||
const tagsRegex = /<([\w\d]+)([^>]*)>/gi;
|
||||
|
||||
const referenceStringMatches = referenceString.match(tagsRegex);
|
||||
|
||||
if (!referenceStringMatches) {
|
||||
console.warn(
|
||||
"WARNING(astro-i18next): default slot does not include any HTML tag to interpolate! You should use the `t` function directly."
|
||||
);
|
||||
return localizedString;
|
||||
}
|
||||
|
||||
const referenceTags = [];
|
||||
referenceStringMatches.forEach((tagNode) => {
|
||||
const [, name, attributes] = tagsRegex.exec(tagNode);
|
||||
referenceTags.push({ name, attributes });
|
||||
|
||||
// reset regex state
|
||||
tagsRegex.exec("");
|
||||
});
|
||||
|
||||
let interpolatedString = localizedString;
|
||||
for (let index = 0; index < referenceTags.length; index++) {
|
||||
const referencedTag = referenceTags[index];
|
||||
// Replace opening tags
|
||||
interpolatedString = interpolatedString.replaceAll(
|
||||
`<${index}>`,
|
||||
`<${referencedTag.name}${referencedTag.attributes}>`
|
||||
);
|
||||
// Replace closing tags
|
||||
interpolatedString = interpolatedString.replaceAll(
|
||||
`</${index}>`,
|
||||
`</${referencedTag.name}>`
|
||||
);
|
||||
}
|
||||
|
||||
return interpolatedString;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a reference string from an HTML string. The reverse of interpolate(), for use
|
||||
* with <Trans> when not explicitly setting a key
|
||||
*/
|
||||
export const createReferenceStringFromHTML = (html: string) => {
|
||||
// Allow these tags to carry through to the output
|
||||
const allowedTags = ["strong", "br", "em", "i", "b"];
|
||||
|
||||
let forbiddenStrings: { key: string; str: string }[] = [];
|
||||
if (i18next.options) {
|
||||
forbiddenStrings = [
|
||||
"keySeparator",
|
||||
"nsSeparator",
|
||||
"pluralSeparator",
|
||||
"contextSeparator",
|
||||
]
|
||||
.map((key) => {
|
||||
return {
|
||||
key,
|
||||
str: i18next.options[key],
|
||||
};
|
||||
})
|
||||
.filter(function <T>(val: T | undefined): val is T {
|
||||
return typeof val !== "undefined";
|
||||
});
|
||||
}
|
||||
|
||||
const tagsRegex = /<([\w\d]+)([^>]*)>/gi;
|
||||
|
||||
const referenceStringMatches = html.match(tagsRegex);
|
||||
|
||||
if (!referenceStringMatches) {
|
||||
console.warn(
|
||||
"WARNING(astro-i18next): default slot does not include any HTML tag to interpolate! You should use the `t` function directly."
|
||||
);
|
||||
return html;
|
||||
}
|
||||
|
||||
const referenceTags = [];
|
||||
referenceStringMatches.forEach((tagNode) => {
|
||||
const [, name, attributes] = tagsRegex.exec(tagNode);
|
||||
referenceTags.push({ name, attributes });
|
||||
|
||||
// reset regex state
|
||||
tagsRegex.exec("");
|
||||
});
|
||||
|
||||
let sanitizedString = html.replace(/\s+/g, " ").trim();
|
||||
for (let index = 0; index < referenceTags.length; index++) {
|
||||
const referencedTag = referenceTags[index];
|
||||
if (
|
||||
allowedTags.includes(referencedTag.name) &&
|
||||
referencedTag.attributes.trim().length === 0
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
sanitizedString = sanitizedString.replaceAll(
|
||||
new RegExp(`<${referencedTag.name}[^>]*?\\s*\\/>`, "gi"),
|
||||
`<${index}/>`
|
||||
);
|
||||
sanitizedString = sanitizedString.replaceAll(
|
||||
`<${referencedTag.name}${referencedTag.attributes}>`,
|
||||
`<${index}>`
|
||||
);
|
||||
sanitizedString = sanitizedString.replaceAll(
|
||||
`</${referencedTag.name}>`,
|
||||
`</${index}>`
|
||||
);
|
||||
}
|
||||
|
||||
for (let index = 0; index < forbiddenStrings.length; index++) {
|
||||
const { key, str } = forbiddenStrings[index];
|
||||
if (sanitizedString.includes(str)) {
|
||||
console.warn(
|
||||
`WARNING(astro-i18next): "${str}" was found in a <Trans> translation key, but it is also used as ${key}. Either explicitly set an i18nKey or change the value of ${key}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
return sanitizedString;
|
||||
};
|
||||
|
||||
export const handleTrailingSlash = (
|
||||
path: string,
|
||||
trailingSlash: AstroI18nextConfig["trailingSlash"]
|
||||
) => {
|
||||
if (path === "/") {
|
||||
return path;
|
||||
}
|
||||
|
||||
switch (trailingSlash) {
|
||||
case "always":
|
||||
return path.endsWith("/") ? path : path + "/";
|
||||
case "never":
|
||||
return path.replace(/\/$/, "");
|
||||
default:
|
||||
return path;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Injects the given locale to a path
|
||||
*/
|
||||
export const localizePath = (
|
||||
path: string = "/",
|
||||
locale: string | null = null,
|
||||
base: string = import.meta.env.BASE_URL
|
||||
): string => {
|
||||
if (!locale) {
|
||||
locale = i18next.language;
|
||||
}
|
||||
|
||||
let pathSegments = path.split("/").filter((segment) => segment !== "");
|
||||
const baseSegments = base.split("/").filter((segment) => segment !== "");
|
||||
|
||||
if (
|
||||
JSON.stringify(pathSegments).startsWith(
|
||||
JSON.stringify(baseSegments).replace(/]+$/, "")
|
||||
)
|
||||
) {
|
||||
// remove base from path
|
||||
pathSegments.splice(0, baseSegments.length);
|
||||
}
|
||||
|
||||
path = pathSegments.length === 0 ? "" : pathSegments.join("/");
|
||||
base = baseSegments.length === 0 ? "/" : "/" + baseSegments.join("/") + "/";
|
||||
|
||||
const {
|
||||
flatRoutes,
|
||||
showDefaultLocale,
|
||||
defaultLocale,
|
||||
locales,
|
||||
trailingSlash,
|
||||
} = AstroI18next.config;
|
||||
|
||||
if (!locales.includes(locale)) {
|
||||
console.warn(
|
||||
`WARNING(astro-i18next): "${locale}" locale is not supported, add it to the locales in your astro config.`
|
||||
);
|
||||
return handleTrailingSlash(`${base}${path}`, trailingSlash);
|
||||
}
|
||||
|
||||
if (pathSegments.length === 0) {
|
||||
if (showDefaultLocale) {
|
||||
return handleTrailingSlash(`${base}${locale}`, trailingSlash);
|
||||
}
|
||||
|
||||
return handleTrailingSlash(
|
||||
locale === defaultLocale ? base : `${base}${locale}`,
|
||||
trailingSlash
|
||||
);
|
||||
}
|
||||
|
||||
// check if the path is not already present in flatRoutes
|
||||
if (locale === defaultLocale) {
|
||||
const translatedPathKey = Object.keys(flatRoutes).find(
|
||||
(key) => flatRoutes[key] === "/" + path
|
||||
);
|
||||
if (typeof translatedPathKey !== "undefined") {
|
||||
pathSegments = translatedPathKey
|
||||
.split("/")
|
||||
.filter((segment) => segment !== "");
|
||||
}
|
||||
}
|
||||
|
||||
// remove locale from pathSegments (if there is any)
|
||||
for (const locale of locales) {
|
||||
if (pathSegments[0] === locale) {
|
||||
pathSegments.shift();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// prepend the given locale if it's not the base one (unless showDefaultLocale)
|
||||
if (showDefaultLocale || locale !== defaultLocale) {
|
||||
pathSegments = [locale, ...pathSegments];
|
||||
}
|
||||
|
||||
const localizedPath = base + pathSegments.join("/");
|
||||
|
||||
// is path translated?
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
flatRoutes,
|
||||
localizedPath.replace(/\/$/, "")
|
||||
)
|
||||
) {
|
||||
return handleTrailingSlash(
|
||||
flatRoutes[localizedPath.replace(/\/$/, "")],
|
||||
trailingSlash
|
||||
);
|
||||
}
|
||||
|
||||
return handleTrailingSlash(localizedPath, trailingSlash);
|
||||
};
|
||||
|
||||
/**
|
||||
* Injects the given locale to a url
|
||||
*/
|
||||
export const localizeUrl = (
|
||||
url: string,
|
||||
locale: string | null = null,
|
||||
base: string = import.meta.env.BASE_URL
|
||||
): string => {
|
||||
const [protocol, , host, ...path] = url.split("/");
|
||||
const baseUrl = protocol + "//" + host;
|
||||
|
||||
return baseUrl + localizePath(path.join("/"), locale, base);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the locale detected from a given path
|
||||
*/
|
||||
export const detectLocaleFromPath = (path: string) => {
|
||||
// remove all leading slashes
|
||||
path = path.replace(/^\/+/g, "");
|
||||
|
||||
const { defaultLocale, locales } = AstroI18next.config;
|
||||
|
||||
const pathSegments = path.split("/");
|
||||
|
||||
if (
|
||||
JSON.stringify(pathSegments) === JSON.stringify([""]) ||
|
||||
JSON.stringify(pathSegments) === JSON.stringify(["", ""])
|
||||
) {
|
||||
return defaultLocale;
|
||||
}
|
||||
|
||||
// make a copy of supported locales
|
||||
let otherLocales = [...locales];
|
||||
otherLocales = otherLocales.filter((locale) => locale !== defaultLocale); // remove base locale (first index)
|
||||
|
||||
// loop over all locales except the base one
|
||||
for (const otherLocale of otherLocales) {
|
||||
if (pathSegments[0] === otherLocale) {
|
||||
// if the path starts with one of the other locales, then detected!
|
||||
return otherLocale;
|
||||
}
|
||||
}
|
||||
|
||||
// return default locale by default
|
||||
return defaultLocale;
|
||||
};
|
||||
|
||||
export const deeplyStringifyObject = (obj: object | Array<any>): string => {
|
||||
const isArray = Array.isArray(obj);
|
||||
let str = isArray ? "[" : "{";
|
||||
for (const key in obj) {
|
||||
if (obj[key] === null || obj[key] === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let value = null;
|
||||
|
||||
// see typeof result: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof#description
|
||||
switch (typeof obj[key]) {
|
||||
case "string": {
|
||||
value = `"${obj[key]}"`;
|
||||
break;
|
||||
}
|
||||
case "number":
|
||||
case "boolean": {
|
||||
value = obj[key];
|
||||
break;
|
||||
}
|
||||
case "object": {
|
||||
value = deeplyStringifyObject(obj[key]);
|
||||
break;
|
||||
}
|
||||
case "function": {
|
||||
value = obj[key].toString().replace(/\s+/g, " ");
|
||||
break;
|
||||
}
|
||||
case "symbol": {
|
||||
value = `Symbol("${obj[key].description}")`;
|
||||
break;
|
||||
}
|
||||
/* c8 ignore start */
|
||||
default:
|
||||
break;
|
||||
/* c8 ignore stop */
|
||||
}
|
||||
|
||||
str += isArray ? `${value},` : `"${key}": ${value},`;
|
||||
}
|
||||
return `${str}${isArray ? "]" : "}"}`;
|
||||
};
|
Reference in New Issue
Block a user