Skip to content

Commit

Permalink
feat: add i18next namespaces + validate config before processing it
Browse files Browse the repository at this point in the history
- add base language to config
- add tests to get 100% coverage
- update README to include namespaces and config props
  • Loading branch information
yassinedoghri committed Jun 12, 2022
1 parent ed7c721 commit 10b40cc
Show file tree
Hide file tree
Showing 28 changed files with 510 additions and 115 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ yarn-error.log*

# cache
.eslintcache

# tests
coverage
134 changes: 115 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ websites!

> **Status** [beta version]
>
> You can use it, and feedback is welcome! As integrations in Astro are still
> experimental, note that some breaking changes may be still introduced during
> this phase.
> You can use it, and feedback is more than welcome! As integrations in Astro
> are still experimental, note that some breaking changes may still be
> introduced during this phase.
## Getting started

Expand All @@ -43,10 +43,10 @@ npm install astro-i18next
},
integrations: [
astroI18next({
resourcesPath: "./src/locales/",
baseLanguage: "en",
i18next: {
debug: true,
supportedLngs: ["en", "fr"], // ℹ️ base language is the first one, ie. "en"
debug: true, // convenient during development to check for missing keys
supportedLngs: ["en", "fr"],
},
}),
],
Expand All @@ -63,9 +63,13 @@ npm install astro-i18next
| └-- fr.json # french translation strings
└-- pages
|-- [lang].astro # you may add a dynamic route to generate language routes
└-- index.astro # route for base language (first element in supportedLngs)
└-- index.astro # route for base language
```

ℹ️ This is the **minimal setup** to get you started as fast as possible. If
you'd like to go further, check out the [config props](#config-props) and
[namespaces](#namespaces).

### 3. 🚀 Start translating

You're all set! You may now start translating your website by using
Expand Down Expand Up @@ -132,7 +136,7 @@ i18next.changeLanguage("fr");
}
```

For a more exhaustive example, see the [example astro website](./example/).
For a more exhaustive example, see the [demo project](./example/).

---

Expand Down Expand Up @@ -165,13 +169,14 @@ import { Trans } from "astro-i18next/components";

#### Trans Props

| Prop name | Type | Description |
| --------- | ------ | ------------------------------------------ |
| i18nKey | string | Internationalization key to interpolate to |
| 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) |
| ns | ?string (undefined) | Namespace to use. May also be embedded in i18nKey but not recommended when used in combination with natural language keys. |

#### interpolate function

`interpolate(i18nKey: string, reference: string): string`
`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.
Expand Down Expand Up @@ -199,9 +204,9 @@ import { LanguageSelector } from "astro-i18next/components";

#### LanguageSelector Props

| Prop name | Type | Description |
| --------- | -------- | ------------------------------------------------------------------------------- |
| showFlag | ?boolean | Choose to display the language emoji before language name (defaults to `false`) |
| Prop name | Type (default) | Description |
| --------- | ------------------ | --------------------------------------------------------- |
| showFlag | ?boolean (`false`) | Choose to display the language emoji before language name |

### localizePath function

Expand All @@ -221,10 +226,101 @@ import i18next from "i18next";
i18next.changeLanguage("fr");
---
<a href={localizePath("/about")}>Go to about page ➔</a>
<!-- renders: <a href="/fr/about">Go to about page ➔</a> -->
<a href={localizePath("/about")}>...</a>
<!-- renders: <a href="/fr/about">...</a> -->
```

---

## Namespaces

For larger projects, the namespaces feature allows you to break your
translations into multiple files, thus easier maintenance!

See i18next's documentation on the
[Namespaces principle](https://www.i18next.com/principles/namespaces).

### 1. Configure namespaces

All you have to do is create a folder including the namespaced translation files
for each language instead of a single file per language.

🪄 `astro-i18next` will automagically load the namespaces for you!

```bash
src
├-- locales
| ├-- en # the base language `en` contains 3 namespaces
| | ├-- common.json
| | ├-- home.json
| | └-- about.json
| └-- fr # `fr` language must contain the same namespaces as the base `en` language
| ├-- common.json
| ├-- home.json
| └-- about.json
└-- pages
└-- [...]
```

You can also define the default namespace in your `astro-i18next` config:

```mjs
import { defineConfig } from "astro/config";
import astroI18next from "astro-i18next";

export default defineConfig({
experimental: {
integrations: true,
},
integrations: [
astroI18next({
baseLanguage: "en",
i18next: {
defaultNS: "common", // translation keys will be retrieved in the common.json file by default
supportedLngs: ["en", "fr"],
},
}),
],
});
```

### 2. Using namespaces

1. Using i18next's `t` function

```astro
---
import { t } from "i18next";
---
<p>{t("home:myKey")}</p>
or
<p>{t("myKey", { ns: "home" })}</p>
```

2. Using the [Trans component](#trans-component)

```astro
---
import { Trans } from "astro-i18next/components";
---
<Trans i18nKey="myKey" ns="home">This is a sample key</Trans>
```

## Config Props

`astro-i18next` abstracts 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, feel free to tweak the config!

| Prop name | Type (default) | Description |
| ------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| baseLanguage | string (undefined) | The default language for your website |
| resourcesPath | ?string (`src/resources/locales/`) | The path to your translation files |
| i18next | `InitOptions` (undefined) | The i18next configuration. See [i18next's documentation](https://www.i18next.com/overview/configuration-options) to know more |

## License

Code released under the [MIT License](https://choosealicense.com/licenses/mit/).
Expand All @@ -241,10 +337,10 @@ Copyright (c) 2022-present, Yassine Doghri
[license]:
https://github.com/yassinedoghri/astro-i18next/blob/develop/LICENSE.md
[license-badge]:
https://img.shields.io/github/license/yassinedoghri/astro-i18next
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-brightgreen.svg
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
Expand Down
48 changes: 3 additions & 45 deletions example/README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,4 @@
# Astro Starter Kit: Minimal
# astro-i18next's example website

```
npm init astro -- --template minimal
```

[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/minimal)

> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
## 🚀 Project Structure

Inside of your Astro project, you'll see the following folders and files:

```
/
├── public/
├── src/
│ └── pages/
│ └── index.astro
└── package.json
```

Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page
is exposed as a route based on its file name.

There's nothing special about `src/components/`, but that's where we like to put
any Astro/React/Vue/Svelte/Preact components.

Any static assets, like images, can be placed in the `public/` directory.

## 🧞 Commands

All commands are run from the root of the project, from a terminal:

| Command | Action |
| :---------------- | :------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3000` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |

## 👀 Want to learn more?

Feel free to check [our documentation](https://github.com/withastro/astro) or
jump into our [Discord server](https://astro.build/chat).
[Astro](https://astro.build/) project for `astro-i18next`'s demo website:
https://astro-i18next.yassinedoghri.com/
2 changes: 1 addition & 1 deletion example/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default defineConfig({
sitemap(),
tailwind(),
astroI18next({
resourcesPath: "./src/locales/",
baseLanguage: "en",
i18next: {
debug: true,
supportedLngs: ["en", "fr"],
Expand Down
4 changes: 2 additions & 2 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
"preview": "astro preview"
},
"devDependencies": {
"@astrojs/sitemap": "^0.1.0",
"@astrojs/sitemap": "^0.1.2",
"@astrojs/tailwind": "^0.2.1",
"astro-seo": "^0.5.0",
"astro": "1.0.0-beta.40"
"astro": "1.0.0-beta.44"
}
}
2 changes: 1 addition & 1 deletion example/src/pages/about.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import i18next from "i18next";
import About from "templates:About";
i18next.changeLanguage("en");
i18next.changeLanguage(i18next.options.supportedLngs[0]);
---

<About />
2 changes: 1 addition & 1 deletion example/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import i18next from "i18next";
import Home from "templates:Home";
i18next.changeLanguage("en");
i18next.changeLanguage(i18next.options.supportedLngs[0]);
---

<Home />
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"description": "An astro integration of i18next + some utility components to help you translate your astro websites!",
"scripts": {
"test": "NODE_OPTIONS='--experimental-vm-modules' jest",
"test:coverage": "npm test -- --coverage",
"preview": "astro preview",
"build": "./build.js && npm run typecheck:emit",
"lint": "eslint --ext js,ts,mts src",
Expand Down Expand Up @@ -31,7 +32,8 @@
"./components": "./dist/components/index.d.mts"
},
"keywords": [
"astro",
"astro-component",
"seo",
"i18next",
"i18n",
"internationalization",
Expand Down
4 changes: 2 additions & 2 deletions src/components/LanguageSelector.astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
import i18next from "i18next";
import ISO6391 from "iso-639-1";
import ISO6391, { LanguageCode } from "iso-639-1";
import countryCodeToFlagEmoji from "country-code-to-flag-emoji";
import { localizePath } from "../utils";
Expand All @@ -21,7 +21,7 @@ const { showFlag = false, ...attributes } = Astro.props;
let value = localizePath(pathname, supportedLanguage)
const label = `${
showFlag ? countryCodeToFlagEmoji(supportedLanguage) + " " : ""
}${ISO6391.getNativeName(supportedLanguage)}`
}${ISO6391.getNativeName(supportedLanguage as LanguageCode)}`

return (
<option value={value} selected={supportedLanguage === currentLanguage}>
Expand Down
6 changes: 4 additions & 2 deletions src/components/Trans.astro
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
---
import i18next from "i18next";
import { interpolate } from "../utils";
export interface Props {
i18nKey: string;
ns?: string;
}
const { i18nKey } = Astro.props;
const { i18nKey, ns } = Astro.props;
const referenceString = await Astro.slots.render("default");
---

<Fragment set:html={interpolate(i18nKey, referenceString)} />
<Fragment set:html={interpolate(i18nKey, referenceString, ns)} />
File renamed without changes.
Loading

0 comments on commit 10b40cc

Please sign in to comment.