Skip to content

Commit

Permalink
Merge branch 'canary' into canary
Browse files Browse the repository at this point in the history
  • Loading branch information
aralroca authored Apr 8, 2024
2 parents 3e96897 + 14bb2e5 commit 99e7822
Show file tree
Hide file tree
Showing 18 changed files with 292 additions and 62 deletions.
54 changes: 54 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,60 @@
"contributions": [
"maintenance"
]
},
{
"login": "BandhiyaHardik",
"name": "HardikBandhiya",
"avatar_url": "https://avatars.githubusercontent.com/u/110784317?v=4",
"profile": "https://github.com/BandhiyaHardik",
"contributions": [
"doc"
]
},
{
"login": "timotew",
"name": "Tim O. Peters",
"avatar_url": "https://avatars.githubusercontent.com/u/12928383?v=4",
"profile": "https://www.linkedin.com/in/timotew/",
"contributions": [
"code"
]
},
{
"login": "hydRAnger",
"name": "Li Ming",
"avatar_url": "https://avatars.githubusercontent.com/u/1228449?v=4",
"profile": "https://github.com/hydRAnger",
"contributions": [
"doc"
]
},
{
"login": "acidfernando",
"name": "Fernando García Hernández",
"avatar_url": "https://avatars.githubusercontent.com/u/86410308?v=4",
"profile": "https://github.com/acidfernando",
"contributions": [
"code"
]
},
{
"login": "hichemfantar",
"name": "Hichem Fantar",
"avatar_url": "https://avatars.githubusercontent.com/u/34947993?v=4",
"profile": "https://www.hichemfantar.com/",
"contributions": [
"code"
]
},
{
"login": "huseyinonalcom",
"name": "Huseyin Onal",
"avatar_url": "https://avatars.githubusercontent.com/u/65453275?v=4",
"profile": "https://github.com/huseyinonalcom",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
Expand Down
23 changes: 20 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<a href="https://github.com/aralroca/next-translate/actions?query=workflow%3ACI" alt="Tests status">
<img src="https://github.com/aralroca/next-translate/workflows/CI/badge.svg" /></a>
<a href="https://twitter.com/intent/follow?screen_name=aralroca">
<img src="https://img.shields.io/twitter/follow/aralroca?style=social&logo=twitter"
<img src="https://img.shields.io/twitter/follow/aralroca?style=social&logo=x"
alt="follow on Twitter"></a>

</div>
Expand All @@ -34,6 +34,7 @@
- [3. Configuration](#3-configuration)
- [4. API](#4-api)
- [useTranslation](#usetranslation)
- [createTranslation](#createtranslation)
- [withTranslation](#withtranslation)
- [Trans Component](#trans-component)
- [DynamicNamespaces](#dynamicnamespaces)
Expand Down Expand Up @@ -111,7 +112,7 @@ In order to do this we use a **webpack loader** that loads the necessary transla
- **`getServerSideProps`**. This is the **default method for dynamic pages** like `[slug].js` or `[...catchall].js`. This is because for these pages it is necessary to define the `getStaticPaths` and there is no knowledge of how the slugs should be for each locale. Likewise, how is it by default, only that you write the getStaticPaths then it will already use the getStaticProps to load the translations.
- **`getInitialProps`**. This is the **default method for these pages that use a HoC**. This is in order to avoid conflicts because HoC could overwrite a `getInitialProps`.

This **whole process is transparent**, so in your pages you can directly consume the `useTranslate` hook to use the namespaces, and you don't need to do anything else.
This **whole process is transparent**, so in your pages you can directly consume the `useTranslation` hook to use the namespaces, and you don't need to do anything else.

If for some reason you use a `getInitialProps` in your `_app.js` file, then the translations will only be loaded into your `getInitialProps` from `_app.js`. We recommend that for optimization reasons you don't use this approach unless it is absolutely necessary.

Expand Down Expand Up @@ -299,6 +300,15 @@ The `t` function:
- **ns**: string - Namespace to use when none is embded in the `i18nKey`.
- **Output**: string

### createTranslation

Similar than `useTranslation` but without being a hook. This helper **only works** in **app dir**.

```ts
const { t, lang } = createTranslation('ns1') // default namespace (optional)
const title = t('title')
```

### withTranslation

**Size**: ~560b 📦
Expand Down Expand Up @@ -372,6 +382,7 @@ Or using `components` prop as a object:
- `fallback` - string | string[] - Optional. Fallback i18nKey if the i18nKey doesn't match.
- `defaultTrans` - string - Default translation for the key. If fallback keys are used, it will be used only after exhausting all the fallbacks.
- `ns` - Namespace to use when none is embedded in `i18nKey`
- `returnObjects` - boolean - Get part of the JSON with all the translations. [See more](#7-nested-translations).

In cases where we require the functionality of the `Trans` component, but need a **string** to be interpolated, rather than the output of the `t(props.i18nKey)` function, there is also a `TransText` component, which takes a `text` prop instead of `i18nKey`.

Expand Down Expand Up @@ -769,7 +780,7 @@ In Trans Component:
## 9. Formatter
You can format params using the `interpolation.formatter` config function.
You can format params using the `interpolation.format` config function.
in `i18n.js`:
Expand Down Expand Up @@ -1141,6 +1152,12 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/crs1138"><img src="https://avatars.githubusercontent.com/u/1313681?v=4?s=100" width="100px;" alt="Honza"/><br /><sub><b>Honza</b></sub></a><br /><a href="#maintenance-crs1138" title="Maintenance">🚧</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/BandhiyaHardik"><img src="https://avatars.githubusercontent.com/u/110784317?v=4?s=100" width="100px;" alt="HardikBandhiya"/><br /><sub><b>HardikBandhiya</b></sub></a><br /><a href="https://github.com/aralroca/next-translate/commits?author=BandhiyaHardik" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/timotew/"><img src="https://avatars.githubusercontent.com/u/12928383?v=4?s=100" width="100px;" alt="Tim O. Peters"/><br /><sub><b>Tim O. Peters</b></sub></a><br /><a href="https://github.com/aralroca/next-translate/commits?author=timotew" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hydRAnger"><img src="https://avatars.githubusercontent.com/u/1228449?v=4?s=100" width="100px;" alt="Li Ming"/><br /><sub><b>Li Ming</b></sub></a><br /><a href="https://github.com/aralroca/next-translate/commits?author=hydRAnger" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/acidfernando"><img src="https://avatars.githubusercontent.com/u/86410308?v=4?s=100" width="100px;" alt="Fernando García Hernández"/><br /><sub><b>Fernando García Hernández</b></sub></a><br /><a href="https://github.com/aralroca/next-translate/commits?author=acidfernando" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.hichemfantar.com/"><img src="https://avatars.githubusercontent.com/u/34947993?v=4?s=100" width="100px;" alt="Hichem Fantar"/><br /><sub><b>Hichem Fantar</b></sub></a><br /><a href="https://github.com/aralroca/next-translate/commits?author=hichemfantar" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/huseyinonalcom"><img src="https://avatars.githubusercontent.com/u/65453275?v=4?s=100" width="100px;" alt="Huseyin Onal"/><br /><sub><b>Huseyin Onal</b></sub></a><br /><a href="https://github.com/aralroca/next-translate/commits?author=huseyinonalcom" title="Code">💻</a></td>
</tr>
</tbody>
</table>
Expand Down
69 changes: 69 additions & 0 deletions __tests__/Trans.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,75 @@ describe('Trans', () => {
expect(container.textContent).toContain(expected)
})

test('should work with arrays', () => {
const i18nKey = 'ns:parent.child'
const expectedFirstElement = '<strong>First</strong> element 42'
const expectedSecondElement = '<strong>Second</strong> element 42'
const withSingular = {
parent: {
child: [
'<0>First</0> element {{num}}',
'<0>Second</0> element {{num}}',
],
},
}
const { container } = render(
<TestEnglish
returnObjects
namespaces={{ ns: withSingular }}
i18nKey={i18nKey}
values={{ num: 42 }}
components={[<strong />]}
/>
)
expect(container.innerHTML).toContain(expectedFirstElement)
expect(container.innerHTML).toContain(expectedSecondElement)
})

test('should work with arrays and singulars', () => {
const i18nKey = 'ns:withsingular'
const expected = '<strong>The number</strong> is one'
const withSingular = {
withsingular_0: ['<0>The number</0> is ZERO!'],
withsingular_one: ['<0>The number</0> is one'],
withsingular_other: ['<0>The number</0> is plural'],
}

const { container } = render(
<TestEnglish
returnObjects
namespaces={{ ns: withSingular }}
i18nKey={i18nKey}
values={{ count: 1 }}
components={[<strong />]}
/>
)

expect(container.innerHTML).toContain(expected)
})

test('should work with arrays and plurals', () => {
const i18nKey = 'ns:withsingular'
const expected = '<strong>The number</strong> is plural'
const withSingular = {
withsingular: ['<0>First</0> is not zero'],
withsingular_0: ['<0>The number</0> is ZERO!'],
withsingular_other: ['<0>The number</0> is plural'],
}

const { container } = render(
<TestEnglish
returnObjects
namespaces={{ ns: withSingular }}
i18nKey={i18nKey}
values={{ count: 2 }}
components={[<strong />]}
/>
)

expect(container.innerHTML).toContain(expected)
})

test('should work with nested keys and custom keySeparator', () => {
const i18nKey = 'ns:parent_child'
const expected = 'The number is 42'
Expand Down
30 changes: 20 additions & 10 deletions docs/type-safety.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,32 @@ export interface TranslationsKeys {
// Specify here all the namespaces you have...
}

export interface TypeSafeTranslate<Namespace extends keyof TranslationsKeys>
type TranslationNamespace = keyof TranslationsKeys;

export interface TranslateFunction<Namespace extends TranslationNamespace> {
(
key: TranslationsKeys[Namespace],
...rest: Tail<Parameters<Translate>>
): string
<T extends string>(template: TemplateStringsArray): string
};

export interface TypeSafeTranslate<Namespace extends TranslationNamespace>
extends Omit<I18n, 't'> {
t: {
(
key: TranslationsKeys[Namespace],
...rest: Tail<Parameters<Translate>>
): string
<T extends string>(template: TemplateStringsArray): string
}
t: TranslateFunction<Namespace>
}

declare module 'next-translate/useTranslation' {
export default function useTranslation<
Namespace extends keyof TranslationsKeys
Namespace extends TranslationNamespace
>(namespace: Namespace): TypeSafeTranslate<Namespace>
}

declare module 'next-translate/getT' {
export default function getT<
Namespace extends TranslationNamespace
>(locale?: string, namespace: Namespace): Promise<TranslateFunction<Namespace>>
}
```
Then type safety should work:
Expand All @@ -38,4 +48,4 @@ Then type safety should work:
<img width="282" alt="Screenshot 2023-07-17 at 19 22 00" src="https://github.com/aralroca/next-translate/assets/13313058/616987b4-e49b-4cf2-b511-cdfaba57e1d2">
Reference: https://github.com/aralroca/next-translate/pull/1108
Reference: <https://github.com/aralroca/next-translate/pull/1108>
4 changes: 2 additions & 2 deletions examples/basic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
"build": "next build"
},
"dependencies": {
"next": "13.4.7",
"next": "14.1.0",
"next-translate": "link:../../",
"react": "link:../../node_modules/react",
"react-dom": "link:../../node_modules/react-dom"
},
"devDependencies": {
"next-translate-plugin": "2.4.4"
"next-translate-plugin": "2.6.2"
}
}
18 changes: 9 additions & 9 deletions examples/complex/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@
"analyze": "ANALYZE=true yarn build"
},
"dependencies": {
"@mdx-js/loader": "2.3.0",
"@mdx-js/react": "2.3.0",
"@next/mdx": "13.4.7",
"next": "13.4.7",
"@mdx-js/loader": "3.0.0",
"@mdx-js/react": "3.0.0",
"@next/mdx": "14.1.0",
"next": "14.1.0",
"next-translate": "link:../../",
"react": "link:../../node_modules/react",
"react-dom": "link:../../node_modules/react-dom"
},
"devDependencies": {
"@next/bundle-analyzer": "13.4.7",
"@types/node": "20.3.1",
"@types/react": "18.2.13",
"next-translate-plugin": "2.4.4",
"typescript": "5.1.3"
"@next/bundle-analyzer": "14.1.0",
"@types/node": "20.11.5",
"@types/react": "18.2.48",
"next-translate-plugin": "2.6.2",
"typescript": "5.3.3"
},
"resolutions": {
"webpack": "5.11.1"
Expand Down
32 changes: 23 additions & 9 deletions examples/with-app-directory/next-translate.d.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,37 @@
import type { Paths, I18n, Translate } from 'next-translate'

type Tail<T> = T extends [unknown, ...infer Rest] ? Rest : never;

export interface TranslationsKeys {
// Example with "common" and "home" namespaces in "en" (the default language):
common: Paths<typeof import('./locales/en/common.json')>
home: Paths<typeof import('./locales/en/home.json')>
// Specify here all the namespaces you have...
}

export interface TypeSafeTranslate<Namespace extends keyof TranslationsKeys>
type TranslationNamespace = keyof TranslationsKeys;

export interface TranslateFunction<Namespace extends TranslationNamespace> {
(
key: TranslationsKeys[Namespace],
...rest: Tail<Parameters<Translate>>
): string
<T extends string>(template: TemplateStringsArray): string
};

export interface TypeSafeTranslate<Namespace extends TranslationNamespace>
extends Omit<I18n, 't'> {
t: {
(
key: TranslationsKeys[Namespace],
...rest: Tail<Parameters<Translate>>
): string
<T extends string>(template: TemplateStringsArray): string
}
t: TranslateFunction<Namespace>
}

declare module 'next-translate/useTranslation' {
export default function useTranslation<
Namespace extends keyof TranslationsKeys
Namespace extends TranslationNamespace
>(namespace: Namespace): TypeSafeTranslate<Namespace>
}

declare module 'next-translate/getT' {
export default function getT<
Namespace extends TranslationNamespace
>(locale?: string, namespace: Namespace): Promise<TranslateFunction<Namespace>>
}
18 changes: 9 additions & 9 deletions examples/with-app-directory/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@
"analyze": "ANALYZE=true yarn build"
},
"dependencies": {
"@mdx-js/loader": "2.3.0",
"@mdx-js/react": "2.3.0",
"@next/mdx": "13.4.7",
"next": "13.4.7",
"@mdx-js/loader": "3.0.0",
"@mdx-js/react": "3.0.0",
"@next/mdx": "14.1.0",
"next": "14.1.0",
"next-translate": "link:../../",
"react": "link:../../node_modules/react",
"react-dom": "link:../../node_modules/react-dom"
},
"devDependencies": {
"@next/bundle-analyzer": "13.4.6",
"@types/node": "20.3.1",
"@types/react": "18.2.12",
"next-translate-plugin": "2.4.4",
"typescript": "5.1.3"
"@next/bundle-analyzer": "14.1.0",
"@types/node": "20.11.5",
"@types/react": "18.2.48",
"next-translate-plugin": "2.6.2",
"typescript": "5.3.3"
},
"resolutions": {
"webpack": "5.11.1"
Expand Down
Loading

0 comments on commit 99e7822

Please sign in to comment.