Skip to content

Commit

Permalink
2.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
lhz516 committed Dec 22, 2024
1 parent 77a9355 commit 40d0b71
Show file tree
Hide file tree
Showing 20 changed files with 3,435 additions and 2,988 deletions.
12 changes: 8 additions & 4 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ module.exports = {
jest: true,
},
globals: {},
plugins: [
'prettier',
// 'jsx-a11y',
],
extends: [
'eslint:recommended',
'plugin:react/recommended',
Expand All @@ -28,6 +24,14 @@ module.exports = {
},
},
rules: {
'prettier/prettier': [
1,
{
singleQuote: true,
endOfLine: 'auto',
semi: false,
},
],
camelcase: [2, { properties: 'never' }],
curly: [2, 'multi-line'],
indent: [2, 2, { SwitchCase: 1 }],
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 2.0.0 and later

Use GitHub Releases page to track changelogs: https://github.com/lhz516/react-simple-i18n/releases

## 1.4.0

- Add `getDefaultText` option in `createI18n`
Expand All @@ -14,6 +18,7 @@
## 1.2.0

- Support React hooks support

## 1.1.0

- Support nested language data
Expand Down
45 changes: 1 addition & 44 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const langData = {
}
```

Choice A: Use React hook
Use React hook
```jsx
import React, { Component } from 'react'
import { createI18n, I18nProvider, useI18n } from 'react-simple-i18n'
Expand All @@ -68,43 +68,6 @@ const App = () => (
)
```

Choice B: Use traditional HOC
```jsx
import React, { Component } from 'react'
import { createI18n, I18nProvider, withI18n } from 'react-simple-i18n'

class Demo extends Component {
handleSetEnglish = () => {
this.props.i18n.setLang('enUS')
}

handleSetChinese = () => {
this.props.i18n.setLang('zhCN')
}

render() {
const { t } = this.props
return (
<div>
<p>{t('projects')}</p>
<p>{t('cars', 'BMW', 'TOYOTA')}</p>
<p>{t('nav.home')}</p>
<button onClick={this.handleSetEnglish}>English</button>
<button onClick={this.handleSetChinese}>中文</button>
</div>
)
}
}

const DemoWithI18n = withI18n(Demo)

const App = () => (
<I18nProvider i18n={createI18n(langData, { lang: 'enUS' })}>
<DemoWithI18n />
</I18nProvider>
)
```

## Top Level API

### createI18n(data, options)
Expand Down Expand Up @@ -143,12 +106,6 @@ Makes `i18n` available to `withI18n` HOC and `useI18n` hook

- `i18n` I18n object created by `createI18n`


### withI18n(Component)

Connects a React component to `i18n` object.
Adds `t` and `i18n` to props of wrapped component.

### useI18n()

A React hook that returns an object with `t` and `i18n`.
Expand Down
8 changes: 2 additions & 6 deletions jest/babel.config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
module.exports = {
presets: [
['@babel/preset-env', { targets: { node: 'current' } }],
[
'@babel/preset-typescript',
],
['@babel/preset-typescript'],
'@babel/preset-react',
],
plugins: [
'@babel/plugin-proposal-class-properties',
],
plugins: ['@babel/plugin-proposal-class-properties'],
}
10 changes: 8 additions & 2 deletions jest/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@ module.exports = {
transform: {
'^.+\\.[tj]sx?$': ['babel-jest', { configFile: './jest/babel.config.js' }],
},
transformIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/\\.pnp\\.[^\\/]+$'],
transformIgnorePatterns: [
'<rootDir>/node_modules/',
'<rootDir>/\\.pnp\\.[^\\/]+$',
],
// moduleNameMapper: {
// '^@utils(.*)$': '<rootDir>/src/utils$1',
// '^@components(.*)$': '<rootDir>/src/components$1',
// '^@reducers(.*)$': '<rootDir>/src/reducers$1',
// '^@actions(.*)$': '<rootDir>/src/actions$1',
// '^.+\\.(css|pcss)$': '<rootDir>/jest/no-op.js',
// },
collectCoverageFrom: ['<rootDir>/src/**/*.js', '!<rootDir>/src/test-utils/**/*.js'],
collectCoverageFrom: [
'<rootDir>/src/**/*.js',
'!<rootDir>/src/test-utils/**/*.js',
],
coverageDirectory: '<rootDir>/jest/coverage',
setupFilesAfterEnv: ['<rootDir>/jest/setup.js'],
}
5 changes: 0 additions & 5 deletions jest/setup.js
Original file line number Diff line number Diff line change
@@ -1,5 +0,0 @@
import { configure } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'

// Configure the enzyme adapter.
configure({ adapter: new Adapter() })
52 changes: 27 additions & 25 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-simple-i18n",
"version": "1.4.0",
"version": "2.0.0",
"description": "React i18n solution with context API",
"main": "lib/index.js",
"module": "es/index.js",
Expand All @@ -10,6 +10,7 @@
"build:cjs": "cross-env NODE_ENV=cjs babel src --out-dir lib --extensions .tsx,.ts",
"build:es": "cross-env NODE_ENV=es babel src --out-dir es --extensions .tsx,.ts",
"build:d.ts": "tsc",
"lint": "eslint . --fix",
"test": "cross-env NODE_ENV=test jest --env=jsdom",
"cov": "cross-env NODE_ENV=test jest --env=jsdom --coverage"
},
Expand All @@ -20,8 +21,7 @@
"setupFilesAfterEnv": [
"./jest/setup.js"
],
"verbose": true,
"testURL": "http://localhost/"
"verbose": true
},
"repository": {
"type": "git",
Expand All @@ -36,37 +36,39 @@
"author": "Hanz Luo",
"license": "MIT",
"peerDependencies": {
"prop-types": "^15.6.0",
"react": "^16.3.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.3.0 || ^17.0.0 || ^18.0.0"
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"devDependencies": {
"@babel/cli": "^7.7.7",
"@babel/core": "^7.7.7",
"@babel/plugin-proposal-class-properties": "^7.7.4",
"@babel/plugin-transform-runtime": "^7.17.0",
"@babel/preset-env": "^7.7.7",
"@babel/preset-react": "^7.7.4",
"@babel/preset-typescript": "^7.16.7",
"@types/enzyme": "^3.10.12",
"@types/jest": "^27.4.1",
"@types/jsdom-global": "^3.0.2",
"@types/react": "^18.0.5",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^10.0.3",
"babel-jest": "^27.5.1",
"cross-env": "^5.2.1",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.1.0",
"@types/jest": "^29",
"@types/jsdom": "^21.1.7",
"@types/react": "^19",
"@types/react-dom": "^19",
"@typescript-eslint/eslint-plugin": "^8",
"@typescript-eslint/parser": "^8.18.1",
"babel-jest": "^29",
"cross-env": "^7",
"eslint": "^8.12.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.17.0",
"jest": "^27.5.1",
"jest": "^29",
"jest-environment-jsdom": "^29.7.0",
"jsdom": "^25.0.1",
"npm-run-all": "^4.1.5",
"prop-types": "^15.7.2",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"regenerator-runtime": "^0.13.9",
"typescript": "^4.1.3",
"utility-types": "^3.10.0"
}
"prettier": "^3.4.2",
"react": "^19",
"react-dom": "^19",
"regenerator-runtime": "^0.14.1",
"typescript": "^5"
},
"dependencies": {}
}
38 changes: 19 additions & 19 deletions src/context.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { createContext } from 'react'

export type handlerFunc = (arg?: object) => void
export type listenerFunc = (func: handlerFunc) => void
export type translateFunc = (key: string, ...args: string[]) => string

export interface I18n {
t: translateFunc
getLang: () => string
setLang: (lang: string) => void
listen: listenerFunc
unlisten: listenerFunc
addLangData: (data: object) => void
_getListenHandlers: () => Array<() => void>
}

const context = createContext<I18n>(null)

export default context
import { createContext } from 'react'

export type handlerFunc = (arg?: object) => void
export type listenerFunc = (func: handlerFunc) => void
export type translateFunc = (key: string, ...args: string[]) => string

export interface I18n {
t: translateFunc
getLang: () => string
setLang: (lang: string) => void
listen: listenerFunc
unlisten: listenerFunc
addLangData: (data: object) => void
_getListenHandlers: () => Array<() => void>
}

const context = createContext<I18n>(null)

export default context
27 changes: 18 additions & 9 deletions src/create-i18n.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ describe('createI18n', () => {

global.console.error = jest.fn()
i18n.setLang('enUS')
expect(global.console.error).toBeCalled()
expect(global.console.error).toHaveBeenCalled()
expect(() => i18n.t('hello')).toThrow()

i18n.addLangData(testLangData)
i18n.setLang('enUS')
expect(i18n.t('hello')).toBe('Hello')
expect(i18n.t('home.about')).toBe('home.about')
// eslint-disable-next-line no-undefined
expect(() => i18n.t(undefined)).toThrow()
expect(() => i18n.t(null)).toThrow()
})
Expand All @@ -45,8 +46,12 @@ describe('createI18n', () => {
expect(i18n.t('greetings', 'Hanz')).toBe('Welcome back! Hanz')
expect(i18n.t('cars')).toBe('This car is %s%, that car is %s%')
expect(i18n.t('cars', 'BMW')).toBe('This car is BMW, that car is %s%')
expect(i18n.t('cars', 'BMW', 'TOYOTA')).toBe('This car is BMW, that car is TOYOTA')
expect(i18n.t('cars', 'BMW', 'TOYOTA', 'HONDA')).toBe('This car is BMW, that car is TOYOTA')
expect(i18n.t('cars', 'BMW', 'TOYOTA')).toBe(
'This car is BMW, that car is TOYOTA',
)
expect(i18n.t('cars', 'BMW', 'TOYOTA', 'HONDA')).toBe(
'This car is BMW, that car is TOYOTA',
)
i18n.setLang('zhCN')
expect(i18n.t('greetings', 'Hanz')).toBe('欢迎回来! Hanz')
expect(i18n.t('cars', 'BMW', 'TOYOTA')).toBe('这辆车是BMW,那辆车是TOYOTA')
Expand All @@ -69,7 +74,6 @@ describe('createI18n', () => {
i18n.setLang('enUS')
expect(i18n.getLang()).toBe('enUS')
expect(mockOnLanguageChange).toHaveBeenCalledTimes(1)


i18n.unlisten(mockOnLanguageChange)
expect(i18n._getListenHandlers().length).toBe(0)
Expand All @@ -86,19 +90,24 @@ describe('createI18n', () => {

it('should throw error if call with invalid language data and options', () => {
expect(() => createI18n(null)).toThrow()
// @ts-ignore
expect(() => createI18n({}, 123)).toThrow()
expect(() => createI18n({}, null)).toThrow()
})

it('should test different cases of getI18nValue', () => {
expect(getI18nValue(testLangData.enUS, 'nav.home')).toBe('Home')
expect(getI18nValue(testLangData.enUS, 'nav.home', key => `EMPTY_${key}`)).toBe('Home')
expect(
getI18nValue(testLangData.enUS, 'nav.home', (key) => `EMPTY_${key}`),
).toBe('Home')
expect(getI18nValue(testLangData.zhCN, 'nav.home')).toBe('首页')
expect(getI18nValue(testLangData.enUS, 'nav')).toBe('nav')
expect(getI18nValue(testLangData.enUS, 'nav.about')).toBe('nav.about')
expect(getI18nValue(testLangData.enUS, 'nav.about', key => `EMPTY_${key}`)).toBe('EMPTY_nav.about')
expect(
getI18nValue(testLangData.enUS, 'nav.about', (key) => `EMPTY_${key}`),
).toBe('EMPTY_nav.about')
expect(getI18nValue(testLangData.enUS, 'hello')).toBe('Hello')
expect(getI18nValue({}, '', key => 'No translation')).toBe('No translation')
expect(getI18nValue({}, '', (key) => 'No translation')).toBe(
'No translation',
)
expect(getI18nValue({}, '')).toBe('')
expect(getI18nValue({}, 'key')).toBe('key')
expect(getI18nValue({ a: 'hi' }, 'key')).toBe('key')
Expand Down
Loading

0 comments on commit 40d0b71

Please sign in to comment.