Skip to content

Commit

Permalink
feat: Type safe messages (#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
amannn authored Apr 1, 2022
1 parent f410247 commit 13b49b1
Show file tree
Hide file tree
Showing 56 changed files with 4,081 additions and 4,383 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.



## 2.4.1 (2022-03-24)


Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<br>
</h1>

> A minimal, but complete solution for managing translations, date, time and number formatting in Next.js apps.
> A minimal, but complete solution for internationalization in Next.js apps.
![Gzipped size](https://badgen.net/bundlephobia/minzip/next-intl) ![Tree shaking supported](https://badgen.net/bundlephobia/tree-shaking/next-intl) [<img src="https://img.shields.io/npm/dw/next-intl.svg" />](https://www.npmjs.com/package/next-intl)

Expand All @@ -18,6 +18,7 @@ This library complements the [internationalized routing](https://nextjs.org/docs
- 🌟 **Proven ICU syntax**: This covers interpolation, plurals, ordinal pluralization, label selection based on enums and rich text. I18n is an essential part of the user experience, therefore this library doesn't compromise on flexibility and never leaves you behind when you need to fine tune a translation.
- πŸ“… **Built-in date, time and number formatting**: You can use global formats for a consistent look & feel of your app and integrate them with translations.
- πŸ’‘ **Hooks-only API**: This ensures that you can use the same API for `children` as well as for attributes which expect strings.
- βœ… **Type-safe**: If you're using TypeScript, you'll benefit from autocompletion for available message keys and compile-time errors for typos.
- βš”οΈ **Battle-tested building blocks**: This library is a minimal wrapper around built-in browser APIs and supplemental lower-level APIs from Format.JS.
- πŸš€ **Fast**: By integrating with both static as well as server side rendering you always get the best possible performance from your app.

Expand Down
Binary file added media/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified media/logo.sketch
Binary file not shown.
Binary file added media/og-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added media/twitter-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"name": "root",
"private": true,
"scripts": {
"publish": "lerna publish --yes --no-verify-access"
"publish": "lerna publish --yes --no-verify-access",
"prerelease": "lerna publish prerelease --no-verify-access --dist-tag next"
},
"workspaces": [
"packages/use-intl",
Expand Down
8 changes: 6 additions & 2 deletions packages/example-advanced/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
require('eslint-config-molindo/setupPlugins');

module.exports = {
extends: ['molindo/typescript', 'molindo/react', 'plugin:@next/next/recommended'],
extends: [
'molindo/typescript',
'molindo/react',
'plugin:@next/next/recommended'
],
env: {
node: true
},
Expand All @@ -10,4 +14,4 @@ module.exports = {
'jsx-a11y/anchor-is-valid': 'off',
'react/display-name': 'off'
}
}
};
1 change: 1 addition & 0 deletions packages/example-advanced/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/node_modules
/.next/
.DS_Store
tsconfig.tsbuildinfo
2 changes: 2 additions & 0 deletions packages/example-advanced/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.



## 2.4.1 (2022-03-24)


Expand Down
3 changes: 3 additions & 0 deletions packages/example-advanced/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Declaring this interface provides type safety for message keys
type Messages = typeof import('./messages/en.json');
declare interface IntlMessages extends Messages {}
8 changes: 8 additions & 0 deletions packages/example-advanced/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,13 @@
},
"PageLayout": {
"pageTitle": "next-intl"
},
"StrictTypes": {
"nested": {
"hello": "Hello",
"another": {
"level": "Level"
}
}
}
}
7 changes: 4 additions & 3 deletions packages/example-advanced/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@
"next": "^12.0.1",
"next-intl": "^2.4.1",
"react": "^17.0.0",
"react-dom": "^17.0.0"
"react-dom": "^17.0.0",
"typescript": "^4.6.3"
},
"devDependencies": {
"@testing-library/react": "12.1.2",
"@types/lodash": "4.14.176",
"@types/react": "17.0.38",
"eslint": "7.4.0",
"eslint-config-molindo": "5.0.1",
"eslint": "8.12.0",
"eslint-config-molindo": "6.0.0",
"eslint-config-next": "^12.0.0",
"jest": "27.4.5"
}
Expand Down
135 changes: 135 additions & 0 deletions packages/example-advanced/src/pages/strict-types.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import {NextIntlProvider, useTranslations} from 'next-intl';

// This page acts as a test environment for the TypeScript integration

function Suite() {
/**
* `t`
*/

// Correct property access
useTranslations('StrictTypes')('nested.hello');
useTranslations('StrictTypes.nested')('another.level');
useTranslations('About')('title');
useTranslations('About')('lastUpdated');
useTranslations('Navigation')('about');
useTranslations()('About.title');
useTranslations()('Navigation.about');
useTranslations('NotFound')('title');
useTranslations('PageLayout')('pageTitle');

// Template strings
const t = useTranslations('StrictTypes');
function getTranslation(nestedKey: 'hello' | 'another.level') {
return t(`nested.${nestedKey}`);
}
getTranslation('hello');
getTranslation('another.level');

// @ts-expect-error Trying to access a child key without a namespace
useTranslations()('title');

// @ts-expect-error Only partial namespaces are allowed
useTranslations('About.title');

// @ts-expect-error Trying to access a key from another namespace
useTranslations('Test')('title');

// @ts-expect-error Invalid namespace
useTranslations('Unknown');

// @ts-expect-error Invalid key on global namespace
useTranslations()('unknown');

// @ts-expect-error Invalid key on valid namespace
useTranslations('About')('unknown');

// @ts-expect-error Invalid namespace and invalid key
useTranslations('unknown')('unknown');

/**
* `t.rich`
*/

// Correct property access
useTranslations('StrictTypes').rich('nested.hello');
useTranslations('StrictTypes.nested').rich('another.level');
useTranslations('About').rich('title');
useTranslations('About').rich('lastUpdated');
useTranslations('Navigation').rich('about');
useTranslations().rich('About.title');
useTranslations().rich('Navigation.about');
useTranslations('NotFound').rich('title');
useTranslations('PageLayout').rich('pageTitle');

// @ts-expect-error Trying to access a child key without a namespace
useTranslations().rich('title');

// @ts-expect-error Only partial namespaces are allowed
useTranslations('About.title');

// @ts-expect-error Trying to access a key from another namespace
useTranslations('Test').rich('title');

// @ts-expect-error Invalid namespace
useTranslations('Unknown');

// @ts-expect-error Invalid key on global namespace
useTranslations().rich('unknown');

// @ts-expect-error Invalid key on valid namespace
useTranslations('About').rich('unknown');

// @ts-expect-error Invalid namespace and invalid key
useTranslations('unknown').rich('unknown');

/**
* `t.raw`
*/

// Correct property access
useTranslations('StrictTypes').raw('nested.hello');
useTranslations('StrictTypes.nested').raw('another.level');
useTranslations('About').raw('title');
useTranslations('About').raw('lastUpdated');
useTranslations('Navigation').raw('about');
useTranslations().raw('About.title');
useTranslations().raw('Navigation.about');
useTranslations('NotFound').raw('title');
useTranslations('PageLayout').raw('pageTitle');

// @ts-expect-error Trying to access a child key without a namespace
useTranslations().raw('title');

// @ts-expect-error Only partial namespaces are allowed
useTranslations('About.title');

// @ts-expect-error Trying to access a key from another namespace
useTranslations('Test').raw('title');

// @ts-expect-error Invalid namespace
useTranslations('Unknown');

// @ts-expect-error Invalid key on global namespace
useTranslations().raw('unknown');

// @ts-expect-error Invalid key on valid namespace
useTranslations('About').raw('unknown');

// @ts-expect-error Invalid namespace and invalid key
useTranslations('unknown').raw('unknown');

return <>Suite passed</>;
}

export default function Test() {
function onError() {
// No-op
}

return (
<NextIntlProvider messages={{}} onError={onError}>
<Suite />
</NextIntlProvider>
);
}
3 changes: 2 additions & 1 deletion packages/example-advanced/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
"jsx": "preserve",
"incremental": true
},
"include": [
"next-env.d.ts",
Expand Down
4 changes: 2 additions & 2 deletions packages/example-remix/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
"@types/accept-language-parser": "^1.5.3",
"@types/react": "^17.0.24",
"@types/react-dom": "^17.0.9",
"eslint": "7.4.0",
"eslint-config-molindo": "5.0.1",
"eslint": "8.12.0",
"eslint-config-molindo": "6.0.0",
"eslint-config-next": "^12.0.0",
"typescript": "^4.1.2"
},
Expand Down
6 changes: 0 additions & 6 deletions packages/example/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## 2.4.1 (2022-03-24)


### Bug Fixes

* Overwrite prerelease ([6caf5c4](https://github.com/amannn/next-intl/commit/6caf5c48a35179f802503bc6580469187765c837))



Expand Down
7 changes: 4 additions & 3 deletions packages/example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
"next": "^12.0.1",
"next-intl": "^2.4.1",
"react": "^17.0.0",
"react-dom": "^17.0.0"
"react-dom": "^17.0.0",
"typescript": "^4.6.3"
},
"devDependencies": {
"@types/lodash": "4.14.176",
"@types/react": "17.0.38",
"eslint": "7.4.0",
"eslint-config-molindo": "5.0.1",
"eslint": "8.12.0",
"eslint-config-molindo": "6.0.0",
"eslint-config-next": "^12.0.0"
}
}
2 changes: 2 additions & 0 deletions packages/next-intl/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.



## 2.4.1 (2022-03-24)


Expand Down
15 changes: 9 additions & 6 deletions packages/next-intl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
"url": "https://github.com/amannn/next-intl"
},
"scripts": {
"start": "tsdx watch",
"build": "tsdx build",
"test": "TZ=Europe/Berlin tsdx test",
"start": "dts watch",
"build": "dts build",
"test": "TZ=Europe/Berlin dts test",
"lint": "eslint src test && tsc",
"prepublishOnly": "yarn test && yarn lint && yarn build && cp ../../README.md ."
},
Expand All @@ -36,6 +36,9 @@
"next",
"next.js"
],
"jest": {
"testEnvironment": "jsdom"
},
"dependencies": {
"use-intl": "^2.4.1"
},
Expand All @@ -46,12 +49,12 @@
"devDependencies": {
"@testing-library/react": "^11.1.2",
"@types/react": "^17.0.33",
"eslint": "7.4.0",
"eslint-config-molindo": "5.0.1",
"dts-cli": "1.4.0",
"eslint": "8.12.0",
"eslint-config-molindo": "6.0.0",
"next": "^11.0.0",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"tsdx": "^0.14.1",
"tslib": "^2.3.1",
"typescript": "^4.4.4"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/use-intl/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ require('eslint-config-molindo/setupPlugins');

module.exports = {
extends: ['molindo/typescript', 'molindo/react']
}
};
4 changes: 4 additions & 0 deletions packages/use-intl/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.





## 2.4.1 (2022-03-24)


Expand Down
1 change: 1 addition & 0 deletions packages/use-intl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- 🌟 **Proven [ICU syntax](https://formatjs.io/docs/core-concepts/icu-syntax)**: This covers interpolation, plurals, ordinal pluralization, label selection based on enums and rich text. I18n is an essential part of the user experience, therefore this library doesn't compromise on flexibility and never leaves you behind when you need to fine tune a translation.
- πŸ“… **Built-in date, time and number formatting**: You can use global formats for a consistent look & feel of your app and integrate them with translations.
- πŸ’‘ **Hooks-only API**: This ensures that you can use the same API for `children` as well as for attributes which expect strings.
- βœ… **Type-safe**: If you're using TypeScript, you'll benefit from autocompletion for available message keys and compile-time errors for typos.
- βš”οΈ **Battle-tested building blocks**: This library is a minimal wrapper around built-in browser APIs and supplemental lower-level APIs from [Format.JS](https://formatjs.io/) (used by `react-intl`).

## What does it look like?
Expand Down
Loading

1 comment on commit 13b49b1

@vercel
Copy link

@vercel vercel bot commented on 13b49b1 Apr 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.