diff --git a/README.md b/README.md
index 134c2729..f4f777ca 100644
--- a/README.md
+++ b/README.md
@@ -181,6 +181,7 @@ Spec | Description
[portal](cypress/component/advanced/portal) | Component test for `ReactDOM.createPortal` feature
[react-bootstrap](cypress/component/advanced/react-bootstrap) | Confirms [react-bootstrap](https://react-bootstrap.github.io/) components are working
[select React component](cypress/component/advanced/react-book-example/src/components/ProductsList.spec.js) | Uses [cypress-react-selector](https://github.com/abhinaba-ghosh/cypress-react-selector) to find DOM elements using React component name and state values
+[i18n](cypress/component/advanced/i18n) | Uses[react-i18next](https://react.i18next.com/) for localizaiton.
### Full examples
diff --git a/cypress/component/advanced/i18n/App.tsx b/cypress/component/advanced/i18n/App.tsx
new file mode 100644
index 00000000..c782ac1c
--- /dev/null
+++ b/cypress/component/advanced/i18n/App.tsx
@@ -0,0 +1,12 @@
+import * as React from 'react'
+import { I18nextProvider } from 'react-i18next'
+import i18n from './i18n'
+import { LocalizedComponent } from './LocalizedComponent'
+
+export function App() {
+ return (
+
+
+
+ )
+}
diff --git a/cypress/component/advanced/i18n/LocalizedComponent.tsx b/cypress/component/advanced/i18n/LocalizedComponent.tsx
new file mode 100644
index 00000000..a2751bfd
--- /dev/null
+++ b/cypress/component/advanced/i18n/LocalizedComponent.tsx
@@ -0,0 +1,21 @@
+import * as React from 'react'
+import { useTranslation, Trans } from 'react-i18next'
+
+interface LocalizedComponentProps {
+ name: string
+ count: number
+}
+
+export function LocalizedComponent({ name, count }: LocalizedComponentProps) {
+ // See ./App.tsx for localization setup
+ const { t } = useTranslation()
+
+ return (
+
+ Hello {{ name }} , you have {{ count }} unread message{' '}
+
+ )
+}
diff --git a/cypress/component/advanced/i18n/README.md b/cypress/component/advanced/i18n/README.md
new file mode 100644
index 00000000..da42ecdb
--- /dev/null
+++ b/cypress/component/advanced/i18n/README.md
@@ -0,0 +1,15 @@
+## Localization Example
+
+This example uses [react-i18next](https://react.i18next.com/) for app localization. Make sure that in "real life" application locale related setup performs at the root of application ([App.tsx](./App.tsx)) and the components are using context for localization.
+
+Thats why in tests we also need to wrap our component with the same provider as our application. Using function composition we can create our own `mount` function which wraps the component with all required providers:
+
+```js
+const localizedMount = (node, { locale }) => {
+ mount(
+
+ {node}
+ ,
+ )
+}
+```
diff --git a/cypress/component/advanced/i18n/i18n.ts b/cypress/component/advanced/i18n/i18n.ts
new file mode 100644
index 00000000..b65e21c2
--- /dev/null
+++ b/cypress/component/advanced/i18n/i18n.ts
@@ -0,0 +1,33 @@
+import i18n from 'i18next'
+import { initReactI18next } from 'react-i18next'
+
+i18n
+ .use(initReactI18next) // passes i18n down to react-i18next
+ .init({
+ resources: {
+ en: {
+ translation: {
+ userMessagesUnread:
+ 'Hello <1>{{name}}1>, you have {{count}} unread message.',
+ userMessagesUnread_plural:
+ 'Hello <1>{{name}}1>, you have {{count}} unread messages.',
+ },
+ },
+ ru: {
+ translation: {
+ userMessagesUnread:
+ 'Привет, <1>{{name}}1>, y тебя {{count}} непрочитанное сообщение.',
+ userMessagesUnread_plural:
+ 'Привет, <1>{{name}}1>, y тебя {{count}} непрочитанных сообщений.',
+ },
+ },
+ },
+ lng: 'en',
+ fallbackLng: 'en',
+
+ interpolation: {
+ escapeValue: false,
+ },
+ })
+
+export default i18n
diff --git a/cypress/component/advanced/i18n/i18next-spec.js b/cypress/component/advanced/i18n/i18next-spec.js
new file mode 100644
index 00000000..4ff7a529
--- /dev/null
+++ b/cypress/component/advanced/i18n/i18next-spec.js
@@ -0,0 +1,48 @@
+///
+import * as React from 'react'
+import i18n from './i18n'
+import { LocalizedComponent } from './LocalizedComponent'
+import { mount } from 'cypress-react-unit-test'
+import { I18nextProvider } from 'react-i18next'
+
+describe('i18n', () => {
+ const localizedMount = (node, { locale }) => {
+ mount(
+
+ {node}
+ ,
+ )
+ }
+
+ it('Plural in en', () => {
+ localizedMount(, {
+ locale: 'en',
+ })
+
+ cy.contains('Hello Josh, you have 15 unread messages.')
+ })
+
+ it('Single in en', () => {
+ localizedMount(, {
+ locale: 'en',
+ })
+
+ cy.contains('Hello Josh, you have 1 unread message.')
+ })
+
+ it('Plural in ru', () => {
+ localizedMount(, {
+ locale: 'ru',
+ })
+
+ cy.contains('Привет, Костя, y тебя 15 непрочитанных сообщений.')
+ })
+
+ it('Single in ru', () => {
+ localizedMount(, {
+ locale: 'ru',
+ })
+
+ cy.contains('Привет, Костя, y тебя 1 непрочитанное сообщение.')
+ })
+})
diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js
index 7a40579e..4cb83b47 100644
--- a/cypress/plugins/index.js
+++ b/cypress/plugins/index.js
@@ -5,6 +5,7 @@ const babelConfig = require('../../babel.config.js')
// should we just reuse root webpack config?
const webpackOptions = {
resolve: {
+ extensions: ['.js', '.ts', '.jsx', '.tsx'],
alias: {
react: path.resolve('./node_modules/react'),
},
diff --git a/package-lock.json b/package-lock.json
index db746c20..3493ef51 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6962,7 +6962,7 @@
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
- "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "integrity": "sha1-+PLIh60Qv2f2NPAFtph/7TF5qsg=",
"dev": true
},
"accepts": {
@@ -7319,7 +7319,7 @@
"aproba": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
- "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+ "integrity": "sha1-aALmJk79GMeQobDVF/DyYnvyyUo=",
"dev": true
},
"arch": {
@@ -8764,7 +8764,7 @@
"babylon": {
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
- "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
+ "integrity": "sha1-ry87iPpvXB5MY00aD46sT1WzleM=",
"dev": true
},
"backo2": {
@@ -8988,7 +8988,7 @@
"bn.js": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
- "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
+ "integrity": "sha1-LN4J617jQfSEdGuwMJsyU7GxRC8="
},
"body-parser": {
"version": "1.19.0",
@@ -9308,7 +9308,7 @@
"browserify-zlib": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
- "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+ "integrity": "sha1-KGlFnZqjviRf6P4sofRuLn9U1z8=",
"requires": {
"pako": "~1.0.5"
}
@@ -9788,7 +9788,7 @@
"cipher-base": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
- "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+ "integrity": "sha1-h2Dk7MJy9MNjUy+SbYdKriwTl94=",
"requires": {
"inherits": "^2.0.1",
"safe-buffer": "^5.0.1"
@@ -10868,7 +10868,7 @@
"crypto-browserify": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
- "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+ "integrity": "sha1-OWz58xN/A+S45TLFj2mCVOAPgOw=",
"requires": {
"browserify-cipher": "^1.0.0",
"browserify-sign": "^4.0.0",
@@ -13856,7 +13856,7 @@
"evp_bytestokey": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
- "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+ "integrity": "sha1-f8vbGY3HGVlDLv4ThCaE4FJaywI=",
"requires": {
"md5.js": "^1.3.4",
"safe-buffer": "^5.1.1"
@@ -14569,7 +14569,7 @@
"find-root": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
- "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
+ "integrity": "sha1-q8/Iunb3CMQql7PWhbfpRQv7nOQ=",
"dev": true
},
"find-up": {
@@ -16686,6 +16686,14 @@
}
}
},
+ "html-parse-stringify2": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/html-parse-stringify2/-/html-parse-stringify2-2.0.1.tgz",
+ "integrity": "sha1-3FZwtyksoVi3vJFsmmc1rIhyg0o=",
+ "requires": {
+ "void-elements": "^2.0.1"
+ }
+ },
"html-webpack-plugin": {
"version": "4.0.0-beta.11",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.11.tgz",
@@ -17116,6 +17124,29 @@
"integrity": "sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ==",
"dev": true
},
+ "i18next": {
+ "version": "19.7.0",
+ "resolved": "https://registry.npmjs.org/i18next/-/i18next-19.7.0.tgz",
+ "integrity": "sha512-sxZhj6u7HbEYOMx81oGwq5MiXISRBVg2wRY3n6YIbe+HtU8ydzlGzv6ErHdrRKYxATBFssVXYbc3lNZoyB4vfA==",
+ "requires": {
+ "@babel/runtime": "^7.10.1"
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.11.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz",
+ "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==",
+ "requires": {
+ "regenerator-runtime": "^0.13.4"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.7",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
+ "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
+ }
+ }
+ },
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -22236,7 +22267,7 @@
"miller-rabin": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
- "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+ "integrity": "sha1-8IA1HIZbDcViqEYpZtqlNUPHik0=",
"requires": {
"bn.js": "^4.0.0",
"brorand": "^1.0.1"
@@ -26682,7 +26713,7 @@
"npmlog": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
- "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+ "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=",
"dev": true,
"requires": {
"are-we-there-yet": "~1.1.2",
@@ -29599,7 +29630,7 @@
"private": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
- "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg=="
+ "integrity": "sha1-I4Hts2ifelPWUxkAYPz4ItLzaP8="
},
"process": {
"version": "0.5.2",
@@ -30626,6 +30657,15 @@
"warning": "^3.0.0"
}
},
+ "react-i18next": {
+ "version": "11.7.2",
+ "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-11.7.2.tgz",
+ "integrity": "sha512-Djj3K3hh5Tecla2CI9rLO3TZBYGMFrGilm0JY4cLofAQONCi5TK6nVmUPKoB59n1ZffgjfgJt6zlbE9aGF6Q0Q==",
+ "requires": {
+ "@babel/runtime": "^7.3.1",
+ "html-parse-stringify2": "2.0.1"
+ }
+ },
"react-is": {
"version": "16.8.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.2.tgz",
@@ -36444,6 +36484,11 @@
"resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
"integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="
},
+ "void-elements": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
+ "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w="
+ },
"w3c-hr-time": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
@@ -37250,7 +37295,7 @@
"word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
- "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "integrity": "sha1-YQY29rH3A4kb00dxzLF/uTtHB5w=",
"dev": true
},
"wordwrap": {
diff --git a/package.json b/package.json
index be1f89b3..6efbc00c 100644
--- a/package.json
+++ b/package.json
@@ -133,7 +133,9 @@
"babel-plugin-istanbul": "6.0.0",
"debug": "4.1.1",
"find-webpack": "2.0.0",
+ "i18next": "19.7.0",
"mime-types": "2.1.26",
+ "react-i18next": "11.7.2",
"unfetch": "4.1.0"
},
"release": {