Skip to content

Commit

Permalink
feat: add utility function to localize path + improve components and …
Browse files Browse the repository at this point in the history
…overall DX

- update utility functions + add unit tests
- reduce required config options to get started
- add about page + navigation to example
- improve responsive on example website
- rename github workflows
- add badges to README.md
- update documentation

BREAKING CHANGE: rename i18nextConfig to i18next in config + remove className and baseLanguage props
for LanguageSelector
  • Loading branch information
yassinedoghri committed Jun 6, 2022
1 parent 505185e commit d230f00
Show file tree
Hide file tree
Showing 32 changed files with 612 additions and 198 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build and Deploy Example
name: astro-i18next-deploy-example

on:
push:
Expand Down
58 changes: 40 additions & 18 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Publish Package
name: astro-i18next-publish

on:
push:
Expand All @@ -10,26 +10,40 @@ jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- name: Checkout 🛎️
uses: actions/checkout@v3

- name: Setup node ⚙️
uses: actions/setup-node@v3
with:
node-version: 16
- run: npm ci
- run: npm run typecheck
- run: npm run lint
- run: npm run prettier
- run: npm test

- name: Quality check 👌
run: |
npm ci
npm run typecheck
npm run lint
npm run prettier
npm test
bundle:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- name: Checkout 🛎️
uses: actions/checkout@v3

- name: Setup node ⚙️
uses: actions/setup-node@v3
with:
node-version: 16
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v3

- name: Bundle 📦
run: |
npm ci
npm run build
- name: Upload bundle as artifact 📤
uses: actions/upload-artifact@v3
with:
name: bundle
path: dist/
Expand All @@ -41,18 +55,26 @@ jobs:
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/alpha' || github.ref == 'refs/heads/beta'
needs: [quality, bundle]
steps:
- uses: actions/checkout@v3
- name: Checkout 🛎️
uses: actions/checkout@v3
with:
token: ${{ secrets.GH_TOKEN_SEMANTIC_RELEASE }}
- uses: actions/setup-node@v3

- name: Setup node ⚙️
uses: actions/setup-node@v3
with:
node-version: 16
- uses: actions/download-artifact@v3

- name: Download bundle artifact 📥
uses: actions/download-artifact@v3
with:
name: bundle
path: dist
- run: npm ci
- run: npm run semantic-release

- name: Release & Publish to NPM 🚀
run: |
npm ci
npm run semantic-release
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
97 changes: 82 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
<div align="center">

# 🧪 astro-i18next

An [astro](https://astro.build/) integration of
[i18next](https://www.i18next.com/) + some
[utility components](#utility-components) to help you translate your astro
websites!

</div>

<div align="center">

[![npm-badge]][npm]&nbsp;[![build-badge]][build]&nbsp;[![license-badge]][license]&nbsp;[![contributions-badge]][contributions]&nbsp;[![semantic-release-badge]][semantic-release]&nbsp;[![stars-badge]][stars]

</div>

> **Status** [beta version]
>
> You can use it, and feedback is welcome! As integrations in Astro are still
Expand Down Expand Up @@ -34,18 +44,17 @@ npm install astro-i18next
integrations: [
astroI18next({
resourcesPath: "./src/locales/",
i18nextConfig: {
i18next: {
debug: true,
fallbackLng: ["en", "fr"],
supportedLngs: ["en", "fr"],
supportedLngs: ["en", "fr"], // ℹ️ base language is the first one, ie. "en"
},
}),
],
});
```

2. Create a `locales` folder containing the translation strings as JSONs (the
files must be named with the language code):
2. Create a `locales` folder containing the translation strings as JSONs (⚠️
files must be named as the language code):

```bash
src
Expand All @@ -54,10 +63,10 @@ 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 fallbackLng)
└-- index.astro # route for base language (first element in supportedLngs)
```

### 3. Start translating
### 3. 🚀 Start translating

You're all set! You may now start translating your website by using
[i18next's `t` function](https://www.i18next.com/overview/api#t) or the
Expand Down Expand Up @@ -88,7 +97,7 @@ i18next.changeLanguage("fr");
<Trans i18nKey="home.subtitle">
This is a <em>more complex</em> string to translate, mixed with <strong
>html elements
</strong> such as a <a href="https://example.com/">a cool link</a>!
</strong> such as <a href="https://example.com/">a cool link</a>!
</Trans>
</p>
</body>
Expand Down Expand Up @@ -125,6 +134,8 @@ i18next.changeLanguage("fr");

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

---

## Utility components

### Trans component
Expand All @@ -138,7 +149,7 @@ strings. Inspired by
import { Trans } from "astro-i18next/components";
---
<Trans i18nKey="sampleKey">
<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!
Expand All @@ -158,6 +169,22 @@ import { Trans } from "astro-i18next/components";
| --------- | ------ | ------------------------------------------ |
| i18nKey | string | Internationalization key to interpolate to |

#### interpolate function

`interpolate(i18nKey: string, reference: string): 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!'
);
```

### LanguageSelector component

Unstyled custom select component to choose amongst supported locales.
Expand All @@ -167,20 +194,60 @@ Unstyled custom select component to choose amongst supported locales.
import { LanguageSelector } from "astro-i18next/components";
---
<LanguageSelector baseLanguage="en" className="my-select-class" />
<LanguageSelector showFlag={true} class="my-select-class" />
```

#### LanguageSelector Props

| Prop name | Type | Description |
| ------------ | -------- | ----------------------------------------------------------------------------------------------------- |
| baseLanguage | string | language code that translations are based off of (will redirect to `/` instead of `/[language-code]`) |
| showFlag | ?boolean | choose to display the language emoji before language name (defaults to `false`) |
| className | ?string | class attribute for the `<select>` tag to customize it |
| Prop name | Type | Description |
| --------- | -------- | ------------------------------------------------------------------------------- |
| showFlag | ?boolean | Choose to display the language emoji before language name (defaults to `false`) |

### localizePath function

`localizePath(path: string, locale: string | null = null): string`

Sets a path within a given locale. If the locale param is not specified, the
current language 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")}>Go to about page ➔</a>
<!-- renders: <a href="/fr/about">Go to about page ➔</a> -->
```

## 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/workflow/status/yassinedoghri/astro-i18next/astro-i18next-publish
[license]:
https://github.com/yassinedoghri/astro-i18next/blob/develop/LICENSE.md
[license-badge]:
https://img.shields.io/github/license/yassinedoghri/astro-i18next
[contributions]: https://github.com/yassinedoghri/astro-i18next/issues
[contributions-badge]:
https://img.shields.io/badge/contributions-welcome-brightgreen.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
4 changes: 0 additions & 4 deletions example/.vscode/extensions.json

This file was deleted.

11 changes: 0 additions & 11 deletions example/.vscode/launch.json

This file was deleted.

3 changes: 1 addition & 2 deletions example/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ export default defineConfig({
tailwind(),
astroI18next({
resourcesPath: "./src/locales/",
i18nextConfig: {
i18next: {
debug: true,
fallbackLng: ["en", "fr"],
supportedLngs: ["en", "fr"],
},
}),
Expand Down
3 changes: 2 additions & 1 deletion example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "astro dev",
"dev": "npm link .. && astro dev",
"start": "astro dev",
"build": "astro build",
"build:package": "cd .. && npm run build && cd example",
"preview": "astro preview"
},
"devDependencies": {
Expand Down
11 changes: 11 additions & 0 deletions example/src/components/atoms/Button.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
const { ...attributes } = Astro.props;
const { class: className, ...attrs } = attributes;
---

<a
class={`transition shadow-lg hover:shadow-md hover:shadow-cyan-500/30 shadow-cyan-500/40 py-3 px-5 bg-gradient-to-b from-cyan-50 to-cyan-200 rounded-full font-semibold uppercase text-cyan-900 border-b-2 border-t-2 border-t-white border-b-cyan-400 focus:ring-2 tracking-wider ${className}`}
{...attrs}
><slot></slot>
</a>
9 changes: 9 additions & 0 deletions example/src/components/atoms/Link.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
const { ...attributes } = Astro.props;
---

<a
class="underline hover:no-underline decoration-4 decoration-cyan-500"
{...attributes}
><slot></slot>
</a>
32 changes: 32 additions & 0 deletions example/src/components/molecules/Navigation.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
import i18next, { t } from "i18next";
import { localizePath } from "astro-i18next";
const { ...attributes } = Astro.props;
const { pathname } = Astro.canonicalURL;
const navigationItems = {
"/": t("navigation.home"),
"/about/": t("navigation.about"),
};
---

<nav class="py-2 mt-2 flex gap-x-2" {...attributes}>
{Object.keys(navigationItems).map((key) => {
let className =
"py-2 px-4 uppercase text-sm underline-offset-2 tracking-wider decoration-4 font-semibold"

if (localizePath(key, i18next.language) === pathname) {
className += " underline decoration-cyan-500 "
} else {
className +=
" opacity-75 hover:opacity-100 hover:underline decoration-gray-300"
}

return (
<a class={className} href={localizePath(key, i18next.language)}>
{navigationItems[key]}
</a>
)
})}
</nav>
33 changes: 33 additions & 0 deletions example/src/components/organisms/HeadSEO.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
import { SEO } from "astro-seo";
import { t } from "i18next";
const { href: canonicalUrl } = Astro.canonicalURL;
---

<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<SEO
title={t("seo.title")}
description={t("seo.description")}
canonical={canonicalUrl}
openGraph={{
basic: {
title: t("seo.ogTitle"),
description: t("seo.description"),
type: "website",
image:
"https://astro-i18next.yassinedoghri.com/assets/images/open-graph.jpg",
url: canonicalUrl,
},
image: {
alt: t("seo.ogImageAlt"),
width: "1200",
height: "630",
type: "image/jpeg",
},
}}
twitter={{
card: "summary_large_image",
creator: "@yassinedoghri",
}}
/>
Loading

0 comments on commit d230f00

Please sign in to comment.