From b7179e2fa26381edaebe7310d285ce0d4d6bd8ca Mon Sep 17 00:00:00 2001 From: five Date: Mon, 12 Dec 2022 15:33:33 +0800 Subject: [PATCH 1/2] =?UTF-8?q?add:nextjs=20=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.d.ts | 1 + next.js | 1 + package.json | 14 ++- pnpm-lock.yaml | 254 ++++++++++++++++++++++++++++++++++++++++ src/index.ts | 159 ++++--------------------- src/next.ts | 121 +++++++++++++++++++ tsconfig.build.cjs.json | 1 + tsconfig.build.json | 2 + tsconfig.json | 2 +- 9 files changed, 413 insertions(+), 142 deletions(-) create mode 100644 next.d.ts create mode 100644 next.js create mode 100644 src/next.ts diff --git a/next.d.ts b/next.d.ts new file mode 100644 index 0000000..5c947b7 --- /dev/null +++ b/next.d.ts @@ -0,0 +1 @@ +export * from './dist/cjs/next' \ No newline at end of file diff --git a/next.js b/next.js new file mode 100644 index 0000000..51622b6 --- /dev/null +++ b/next.js @@ -0,0 +1 @@ +module.exports = require('./dist/cjs/next') \ No newline at end of file diff --git a/package.json b/package.json index b5cc2a7..6830ca8 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,18 @@ { "name": "i18next-mobx", - "version": "0.0.0-alpha.3", + "version": "0.0.1", "description": "A management scheme for i18nxet mobx", - "main": "dist/index.js", + "main": "dist/cjs/index.js", + "umd:main": "dist/index.js", + "unpkg": "dist/index.js", + "jsdelivr": "dist/index.js", + "jsnext:main": "dist/esm/index.js", "types": "dist/esm/index.d.ts", "module": "dist/esm/index.js", "scripts": { "start": "ts-node ./src/index.ts -P tsconfig.json --no-cache", - "build:esm": "rm -rf dist/esm && tsc -p tsconfig.build.json", - "build:cjs": "rm -rf dist/cjs && tsc -p tsconfig.build.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json", + "build:esm": "tsc -p tsconfig.build.json", + "build:cjs": "tsc -p tsconfig.build.cjs.json", "build": "tsc -P tsconfig.json", "build:all": "npm run e2e && npm run build && npm run build:esm && npm run build:cjs", "format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"", @@ -38,6 +42,8 @@ "husky": "^8.0.2", "i18next-browser-languagedetector": "^7.0.1", "jest": "^29.3.1", + "mobx-react": "^7.6.0", + "next": "^13.0.6", "prettier": "^2.7.1", "react": "16.8.0", "react-i18next": "^12.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fbe0a7e..900fab3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,6 +9,8 @@ specifiers: i18next-browser-languagedetector: ^7.0.1 jest: ^29.3.1 mobx: 5.x.x + mobx-react: ^7.6.0 + next: ^13.0.6 prettier: ^2.7.1 react: 16.8.0 react-i18next: ^12.0.0 @@ -29,6 +31,8 @@ devDependencies: husky: 8.0.2 i18next-browser-languagedetector: 7.0.1 jest: 29.3.1_70d4953900ede8d3dd438a81bd203eaa + mobx-react: 7.6.0_mobx@5.15.7+react@16.8.0 + next: 13.0.6_react@16.8.0 prettier: 2.8.0 react: 16.8.0 react-i18next: 12.0.0_i18next@22.0.6+react@16.8.0 @@ -666,6 +670,127 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 dev: true + /@next/env/13.0.6: + resolution: {integrity: sha512-yceT6DCHKqPRS1cAm8DHvDvK74DLIkDQdm5iV+GnIts8h0QbdHvkUIkdOvQoOODgpr6018skbmSQp12z5OWIQQ==} + dev: true + + /@next/swc-android-arm-eabi/13.0.6: + resolution: {integrity: sha512-FGFSj3v2Bluw8fD/X+1eXIEB0PhoJE0zfutsAauRhmNpjjZshLDgoXMWm1jTRL/04K/o9gwwO2+A8+sPVCH1uw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@next/swc-android-arm64/13.0.6: + resolution: {integrity: sha512-7MgbtU7kimxuovVsd7jSJWMkIHBDBUsNLmmlkrBRHTvgzx5nDBXogP0hzZm7EImdOPwVMPpUHRQMBP9mbsiJYQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@next/swc-darwin-arm64/13.0.6: + resolution: {integrity: sha512-AUVEpVTxbP/fxdFsjVI9d5a0CFn6NVV7A/RXOb0Y+pXKIIZ1V5rFjPwpYfIfyOo2lrqgehMNQcyMRoTrhq04xg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@next/swc-darwin-x64/13.0.6: + resolution: {integrity: sha512-SasCDJlshglsPnbzhWaIF6VEGkQy2NECcAOxPwaPr0cwbbt4aUlZ7QmskNzgolr5eAjFS/xTr7CEeKJtZpAAtQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@next/swc-freebsd-x64/13.0.6: + resolution: {integrity: sha512-6Lbxd9gAdXneTkwHyYW/qtX1Tdw7ND9UbiGsGz/SP43ZInNWnW6q0au4hEVPZ9bOWWRKzcVoeTBdoMpQk9Hx9w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@next/swc-linux-arm-gnueabihf/13.0.6: + resolution: {integrity: sha512-wNdi5A519e1P+ozEuYOhWPzzE6m1y7mkO6NFwn6watUwO0X9nZs7fT9THmnekvmFQpaZ6U+xf2MQ9poQoCh6jQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@next/swc-linux-arm64-gnu/13.0.6: + resolution: {integrity: sha512-e8KTRnleQY1KLk5PwGV5hrmvKksCc74QRpHl5ffWnEEAtL2FE0ave5aIkXqErsPdXkiKuA/owp3LjQrP+/AH7Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@next/swc-linux-arm64-musl/13.0.6: + resolution: {integrity: sha512-/7RF03C3mhjYpHN+pqOolgME3guiHU5T3TsejuyteqyEyzdEyLHod+jcYH6ft7UZ71a6TdOewvmbLOtzHW2O8A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@next/swc-linux-x64-gnu/13.0.6: + resolution: {integrity: sha512-kxyEXnYHpOEkFnmrlwB1QlzJtjC6sAJytKcceIyFUHbCaD3W/Qb5tnclcnHKTaFccizZRePXvV25Ok/eUSpKTw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@next/swc-linux-x64-musl/13.0.6: + resolution: {integrity: sha512-N0c6gubS3WW1oYYgo02xzZnNatfVQP/CiJq2ax+DJ55ePV62IACbRCU99TZNXXg+Kos6vNW4k+/qgvkvpGDeyA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@next/swc-win32-arm64-msvc/13.0.6: + resolution: {integrity: sha512-QjeMB2EBqBFPb/ac0CYr7GytbhUkrG4EwFWbcE0vsRp4H8grt25kYpFQckL4Jak3SUrp7vKfDwZ/SwO7QdO8vw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@next/swc-win32-ia32-msvc/13.0.6: + resolution: {integrity: sha512-EQzXtdqRTcmhT/tCq81rIwE36Y3fNHPInaCuJzM/kftdXfa0F+64y7FAoMO13npX8EG1+SamXgp/emSusKrCXg==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@next/swc-win32-x64-msvc/13.0.6: + resolution: {integrity: sha512-pSkqZ//UP/f2sS9T7IvHLfEWDPTX0vRyXJnAUNisKvO3eF3e1xdhDX7dix/X3Z3lnN4UjSwOzclAI87JFbOwmQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@sinclair/typebox/0.24.51: resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} dev: true @@ -682,6 +807,12 @@ packages: '@sinonjs/commons': 1.8.6 dev: true + /@swc/helpers/0.4.14: + resolution: {integrity: sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==} + dependencies: + tslib: 2.4.1 + dev: true + /@tsconfig/node10/1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} dev: true @@ -1011,6 +1142,10 @@ packages: resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} dev: true + /client-only/0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + dev: true + /cliui/8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -1950,6 +2085,41 @@ packages: minimist: 1.2.7 dev: true + /mobx-react-lite/3.4.0_mobx@5.15.7+react@16.8.0: + resolution: {integrity: sha512-bRuZp3C0itgLKHu/VNxi66DN/XVkQG7xtoBVWxpvC5FhAqbOCP21+nPhULjnzEqd7xBMybp6KwytdUpZKEgpIQ==} + peerDependencies: + mobx: ^6.1.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + mobx: 5.15.7 + react: 16.8.0 + dev: true + + /mobx-react/7.6.0_mobx@5.15.7+react@16.8.0: + resolution: {integrity: sha512-+HQUNuh7AoQ9ZnU6c4rvbiVVl+wEkb9WqYsVDzGLng+Dqj1XntHu79PvEWKtSMoMj67vFp/ZPXcElosuJO8ckA==} + peerDependencies: + mobx: ^6.1.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: '*' + react-native: '*' + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + dependencies: + mobx: 5.15.7 + mobx-react-lite: 3.4.0_mobx@5.15.7+react@16.8.0 + react: 16.8.0 + dev: true + /mobx/5.15.7: resolution: {integrity: sha512-wyM3FghTkhmC+hQjyPGGFdpehrcX1KOXsDuERhfK2YbJemkUhEB+6wzEN639T21onxlfYBmriA1PFnvxTUhcKw==} dev: false @@ -1958,10 +2128,59 @@ packages: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true + /nanoid/3.3.4: + resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + /natural-compare/1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true + /next/13.0.6_react@16.8.0: + resolution: {integrity: sha512-COvigvms2LRt1rrzfBQcMQ2GZd86Mvk1z+LOLY5pniFtL4VrTmhZ9salrbKfSiXbhsD01TrDdD68ec3ABDyscA==} + engines: {node: '>=14.6.0'} + hasBin: true + peerDependencies: + fibers: '>= 3.1.0' + node-sass: ^6.0.0 || ^7.0.0 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + peerDependenciesMeta: + fibers: + optional: true + node-sass: + optional: true + sass: + optional: true + dependencies: + '@next/env': 13.0.6 + '@swc/helpers': 0.4.14 + caniuse-lite: 1.0.30001434 + postcss: 8.4.14 + react: 16.8.0 + styled-jsx: 5.1.0_react@16.8.0 + optionalDependencies: + '@next/swc-android-arm-eabi': 13.0.6 + '@next/swc-android-arm64': 13.0.6 + '@next/swc-darwin-arm64': 13.0.6 + '@next/swc-darwin-x64': 13.0.6 + '@next/swc-freebsd-x64': 13.0.6 + '@next/swc-linux-arm-gnueabihf': 13.0.6 + '@next/swc-linux-arm64-gnu': 13.0.6 + '@next/swc-linux-arm64-musl': 13.0.6 + '@next/swc-linux-x64-gnu': 13.0.6 + '@next/swc-linux-x64-musl': 13.0.6 + '@next/swc-win32-arm64-msvc': 13.0.6 + '@next/swc-win32-ia32-msvc': 13.0.6 + '@next/swc-win32-x64-msvc': 13.0.6 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + dev: true + /node-int64/0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} dev: true @@ -2076,6 +2295,15 @@ packages: find-up: 4.1.0 dev: true + /postcss/8.4.14: + resolution: {integrity: sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.4 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + /prettier/2.8.0: resolution: {integrity: sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==} engines: {node: '>=10.13.0'} @@ -2228,6 +2456,11 @@ packages: engines: {node: '>=8'} dev: true + /source-map-js/1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + /source-map-support/0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} dependencies: @@ -2290,6 +2523,23 @@ packages: engines: {node: '>=8'} dev: true + /styled-jsx/5.1.0_react@16.8.0: + resolution: {integrity: sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + dependencies: + client-only: 0.0.1 + react: 16.8.0 + dev: true + /supports-color/5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -2410,6 +2660,10 @@ packages: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true + /tslib/2.4.1: + resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} + dev: true + /tslint-config-prettier/1.18.0: resolution: {integrity: sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg==} engines: {node: '>=4.0.0'} diff --git a/src/index.ts b/src/index.ts index 67dadab..8cca1ce 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,15 @@ import { i18n, TOptions } from 'i18next'; import * as _i18next from 'i18next'; -import { observable, action } from 'mobx'; +import { observable, action} from 'mobx'; -import { isServer , isFunction,mixin, isExternalUrl, loadFetch, parseLanguageHeader, parseLanguageHeaderCookie} from "./tools" +import { isServer , isFunction,mixin,loadFetch} from "./tools" -const i18next = (_i18next as unknown as i18n) + const i18next = (_i18next.default as unknown as i18n)||(_i18next as unknown as i18n) interface I18nProxy { changeLanguage: Function; currentLanguage: string; - + init: Function; t: Function; addResourceBundle(lng: string, ns: string, resources: any):I18nProxy; hasResourceBundle(lng: string, ns: string):boolean; @@ -23,15 +23,6 @@ interface I18NextExtendsProxy extends i18n { } -interface ProcessI18NextProxy extends NodeJS.Process { - I18NextProxy?: { severLanguage: string }; -} - -interface I18NextHeaders { - 'accept-language'?: string; - cookie?: string; -} - export class I18NextMobxProxy{ target: T|I18nProxy; @@ -42,6 +33,7 @@ export class I18NextMobxProxy{ @action changeCurrentLanguage=(lng: string) => { + if(globalThis.document)document.documentElement.lang = lng; this.currentLanguage = lng; } @@ -52,7 +44,7 @@ export class I18NextMobxProxy{ (this.target as i18n).addResourceBundle(lng, ns, data); } await (this.target as I18nProxy).changeLanguage(lng); - this.currentLanguage = lng; + this.changeCurrentLanguage(i18next.language); } t = ( @@ -94,139 +86,32 @@ const loadLanguage = async (lng: string) => { return await loadFetch(_loadPath.replace(/\{\{lng\}\}/gi,lng)); }; -const loadServerLanguageData = (lng: string,ns:string="translation") => { - return new Promise(resolve => { - let _loadPath:string ="locales/{{lng}}.json"; - const optionsLoadPath = i18next.options?.backend&&(i18next.options.backend as any).addPath - - if(optionsLoadPath){ - isFunction(optionsLoadPath)?( _loadPath = optionsLoadPath(lng,"translation")):(_loadPath = optionsLoadPath); - } - _loadPath = _loadPath.replace(/\{\{lng\}\}/gi,lng) - - if(isExternalUrl(_loadPath)){ - loadFetch(_loadPath).then((data)=>{ - resolve(data); - }) - }else{ - let data = "{}" - try { - const { readFileSync } = require('fs'); - const path = require('path'); - data = readFileSync( - path.join(process.cwd(), 'public', _loadPath.replace(/\{\{lng\}\}/gi,lng)), - 'utf-8', - ); - } catch (error) {} - resolve(JSON.parse(data)); - } - }); -}; function defaultSetup(){ if (!isServer()) { - i18next.on("languageChanged", function (lng: string) { - if (!i18next.hasResourceBundle(lng, 'translation')) { - loadLanguage(lng).then(data => { - i18next.addResourceBundle(lng, 'translation', data); - i18next.changeLanguage(lng).then(()=>{ - i18nProxy.changeCurrentLanguage(lng) - }) - }); - i18next.off('languageChanged'); - } + i18next.on("languageChanged", function(lng: string) { + if(lng !== i18nProxy.currentLanguage) { + if(!i18next.hasResourceBundle(lng,"translation")){ + loadLanguage(lng).then(data=>{ + i18next.addResourceBundle(lng, 'translation', data) + i18next.changeLanguage(lng).then(()=>{ + i18nProxy.changeCurrentLanguage(i18next.language) + }) + }) + + }else{ + i18nProxy.changeCurrentLanguage(lng) + } + } }); } } -function parseLanguage(headersData: I18NextHeaders) { - const languages = parseLanguageHeader( - headersData['accept-language'] || 'zh-CN,zh;q=0.9', - ); - const cookieLanguage = parseLanguageHeaderCookie( - headersData.cookie, - ); - - return cookieLanguage || languages[0]; -} - -function setI18nConfiguration(HeadersData?: I18NextHeaders) { - if (HeadersData) { - (process as ProcessI18NextProxy).I18NextProxy = { - severLanguage: parseLanguage(HeadersData), - }; - } -} -const loadServerLanguage = async(lng:string)=>{ - if (lng&&!i18next.hasResourceBundle(lng, 'translation')) { - let data = await loadServerLanguageData(lng) - i18next.addResourceBundle(lng, 'translation',data) -} - await i18next.changeLanguage(lng) - i18nProxy.changeCurrentLanguage(lng) -} - -export function getSeverLanguage() { - return (process as ProcessI18NextProxy).I18NextProxy?.severLanguage; -} - -export const withNextServerI18n = (nextAppComponent: { - ({ Component, pageProps }); - getInitialProps?: any; -}) => { - - const newNextAppComponent:{({ Component, pageProps });getInitialProps?: any;} =(function(_nextAppComponent){ - return function(){ - if(isServer()){ - const lng = arguments[0].router.query.i18nextLanguage||i18next.language - loadServerLanguage(lng) - } - return _nextAppComponent.apply(_nextAppComponent, arguments); - } - }(nextAppComponent)) - if (nextAppComponent.getInitialProps) { - newNextAppComponent.getInitialProps = (function (_getInitialProps: { - apply: ( - arg0: { - ({ Component, pageProps }); - getInitialProps?: any; - }, - arg1: IArguments, - ) => void; - }) { - return function () { - const arg = arguments; - const HeadersData = arg[0].ctx.req?.headers; - setI18nConfiguration(HeadersData); - arg[0].ctx.query.i18nextLanguage = getSeverLanguage(); - return new Promise(resolve => { - resolve(_getInitialProps.apply(nextAppComponent, arg)); - }); - }; - })(nextAppComponent.getInitialProps); - } else { - newNextAppComponent.getInitialProps = async ({ - Component, - ctx, - }) => { - const HeadersData = ctx.req?.headers; - setI18nConfiguration(HeadersData); - ctx.query.i18nextLanguage = getSeverLanguage(); - let pageProps = {}; - if (Component.getInitialProps) { - pageProps = await Component.getInitialProps(ctx); - } - return { pageProps }; - }; - } - - - return newNextAppComponent; -}; - export { isServer, + i18next, } + export const t = function ( key: string | string[], defaultValue?: string | undefined, diff --git a/src/next.ts b/src/next.ts new file mode 100644 index 0000000..2785ef6 --- /dev/null +++ b/src/next.ts @@ -0,0 +1,121 @@ +import {i18next,changeLanguage} from "./index"; +import { isExternalUrl, isFunction, isServer, loadFetch, parseLanguageHeader, parseLanguageHeaderCookie } from "./tools"; + +interface ProcessI18NextProxy extends NodeJS.Process { + I18NextProxy?: { severLanguage: string }; +} + +interface I18NextHeaders { + 'accept-language'?: string; + cookie?: string; +} + +function parseLanguage(headersData: I18NextHeaders) { + const languages = parseLanguageHeader( + headersData['accept-language'] || 'zh-CN,zh;q=0.9', + ); + const cookieLanguage = parseLanguageHeaderCookie( + headersData.cookie, + ); + + return cookieLanguage || languages[0]; +} +const loadServerLanguageData = (lng: string,ns:string="translation") => { + return new Promise(resolve => { + let _loadPath:string ="/locales/{{lng}}.json"; + const optionsLoadPath = i18next.options?.backend&&(i18next.options.backend as any).addPath + + if(optionsLoadPath){ + isFunction(optionsLoadPath)?( _loadPath = optionsLoadPath(lng,"translation")):(_loadPath = optionsLoadPath); + } + + if(isExternalUrl(_loadPath)){ + loadFetch(_loadPath).then((data)=>{ + resolve(data); + }) + }else{ + let data = "{}" + try { + const { readFileSync } = require('fs'); + const path = require('path'); + data = readFileSync( + path.join(process.cwd(), 'public', _loadPath.replace(/\{\{lng\}\}/gi,lng)), + 'utf-8', + ); + } catch (error) {} + resolve(JSON.parse(data)); + } + }); +}; + +export const loadServerLanguage = async(lng:string)=>{ + if (lng&&!i18next.hasResourceBundle(lng, 'translation')) { + let data = await loadServerLanguageData(lng) + i18next.addResourceBundle(lng, 'translation',data) + } + await changeLanguage(lng) +} +function setI18nConfiguration(HeadersData?: I18NextHeaders) { + if (HeadersData) { + (process as ProcessI18NextProxy).I18NextProxy = { + severLanguage: parseLanguage(HeadersData), + }; + } +} +export function getSeverLanguage() { + return (process as ProcessI18NextProxy).I18NextProxy?.severLanguage; +} + +export const withNextI18NextMobxApp = (nextAppComponent: { + ({ Component, pageProps }); + getInitialProps?: any; +}) => { + + if (nextAppComponent.getInitialProps) { + nextAppComponent.getInitialProps = (function(_getInitialProps: { + apply: ( + arg0: { + ({ Component, pageProps }); + getInitialProps?: any; + }, + arg1: IArguments, + ) => void; + }) { + return function () { + const arg = arguments; + const HeadersData = arg[0].ctx.req?.headers; + setI18nConfiguration(HeadersData); + arg[0].ctx.query.i18nextLanguage = i18next.options.lng || getSeverLanguage(); + + return new Promise(resolve => { + if(isServer()){ + loadServerLanguage(arg[0].ctx.query.i18nextLanguage||i18next.language).then(()=>{ + resolve(_getInitialProps.apply(nextAppComponent, arg)); + }) + }else{ + resolve(_getInitialProps.apply(nextAppComponent, arg)); + } + }); + }; + })(nextAppComponent.getInitialProps); + } else { + nextAppComponent.getInitialProps = async ({ + Component, + ctx, + }) => { + const HeadersData = ctx.req?.headers; + setI18nConfiguration(HeadersData); + ctx.query.i18nextLanguage = i18next.options.lng || getSeverLanguage(); + if(isServer()){ + await loadServerLanguage(ctx.query.i18nextLanguage || i18next.language) + } + let pageProps = {}; + if (Component.getInitialProps) { + pageProps = await Component.getInitialProps(ctx); + } + return { pageProps }; + }; + } + + return nextAppComponent; +}; diff --git a/tsconfig.build.cjs.json b/tsconfig.build.cjs.json index 0e93767..aacdda3 100644 --- a/tsconfig.build.cjs.json +++ b/tsconfig.build.cjs.json @@ -2,6 +2,7 @@ "extends": "./tsconfig.build.json", "compilerOptions": { "module": "CommonJS", + "target": "ES5", "rootDir": "src", "outDir": "dist/cjs", }, diff --git a/tsconfig.build.json b/tsconfig.build.json index 2f2bc59..bc68e34 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -3,6 +3,8 @@ "compilerOptions": { "rootDir": "src", "outDir": "dist/esm", + "target": "ES6", + "module": "ESNext", "noEmit": false, "declaration": true, "declarationMap": true, diff --git a/tsconfig.json b/tsconfig.json index 17ec958..39711af 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "outDir": "./dist", "target": "ES5", - "module": "CommonJS",//组织代码方式 + "module": "UMD",//组织代码方式 "sourceMap": true, "moduleResolution": "Node", // 模块解决策略 "experimentalDecorators": true, // 开启装饰器定义 From ce4b4d6584c397cbc80e5b8d36b9440b2974ea07 Mon Sep 17 00:00:00 2001 From: five Date: Fri, 16 Dec 2022 20:47:18 +0800 Subject: [PATCH 2/2] =?UTF-8?q?add:nextjs=20=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.ts | 182 ++++++++++++++++++++-------------------------- src/next.ts | 199 +++++++++++++++++++++++++-------------------------- src/tools.ts | 79 +++++++++++++------- 3 files changed, 226 insertions(+), 234 deletions(-) diff --git a/src/index.ts b/src/index.ts index 8cca1ce..db3b512 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,133 +1,105 @@ -import { i18n, TOptions } from 'i18next'; -import * as _i18next from 'i18next'; -import { observable, action} from 'mobx'; +import { i18n, TOptions } from 'i18next' +import * as _i18next from 'i18next' +import { observable, action } from 'mobx' -import { isServer , isFunction,mixin,loadFetch} from "./tools" +import { isServer, isFunction, mixin, loadFetch } from './tools' - const i18next = (_i18next.default as unknown as i18n)||(_i18next as unknown as i18n) +const i18next = (_i18next.default as unknown as i18n) || (_i18next as unknown as i18n) interface I18nProxy { - changeLanguage: Function; - currentLanguage: string; - init: Function; - t: Function; - addResourceBundle(lng: string, ns: string, resources: any):I18nProxy; - hasResourceBundle(lng: string, ns: string):boolean; - on(event: string, listener: (...args: any[]) => void): void; + changeLanguage: i18n['changeLanguage'] + currentLanguage: string + init: i18n['init'] + t: i18n['t'] + addResourceBundle(lng: string, ns: string, resources: any): I18nProxy + hasResourceBundle(lng: string, ns: string): boolean + on(event: string, listener: (...args: any[]) => void): void } interface I18NextExtendsProxy extends i18n { - currentLanguage: string; - changeCurrentLanguage: Function; - i18nMap: Record>; - + currentLanguage: string + changeCurrentLanguage: Function + i18nMap: Record> } -export class I18NextMobxProxy{ - target: T|I18nProxy; - - i18nMap: Record> = {}; - +export class I18NextMobxProxy { + target: T | I18nProxy + + i18nMap: Record> = {} + @observable - currentLanguage: string = ''; + currentLanguage: string = '' - @action - changeCurrentLanguage=(lng: string) => { - if(globalThis.document)document.documentElement.lang = lng; - this.currentLanguage = lng; + @action + changeCurrentLanguage = (lng: string) => { + if (globalThis.document) document.documentElement.lang = lng + this.currentLanguage = lng } @action changeLanguage = async (lng: string, ns: string = 'translation') => { - if (!(this.target as i18n).hasResourceBundle(lng, ns)) { - const data = await loadLanguage(lng); - (this.target as i18n).addResourceBundle(lng, ns, data); - } - await (this.target as I18nProxy).changeLanguage(lng); - this.changeCurrentLanguage(i18next.language); + if (!(this.target as i18n).hasResourceBundle(lng, ns)) { + const data = await loadLanguage(lng) + ;(this.target as i18n).addResourceBundle(lng, ns, data) + } + await (this.target as I18nProxy).changeLanguage(lng) + this.changeCurrentLanguage(i18next.language) } - t = ( - key: string | string[], - defaultValue?: string | undefined, - options?: - | string - | TOptions<{ - returnDetails: true; - returnObjects: true; - }> - | undefined, - ) => { - const keyIdx = Array.isArray(key) ? key.join('=') : key; - - this.i18nMap[this.currentLanguage] ||= {}; - this.i18nMap[this.currentLanguage][keyIdx] ||= (this.target as I18nProxy).t(key, defaultValue, options) - - return this.i18nMap[this.currentLanguage][keyIdx] ?? key; + t = (keys: string | string[], options?: TOptions, lastKey?: string) => { + const keyIdx = Array.isArray(keys) ? keys.join('=') : keys + + this.i18nMap[this.currentLanguage] ||= {} + this.i18nMap[this.currentLanguage][keyIdx] ||= i18next.t(keys, lastKey, options) + + return this.i18nMap[this.currentLanguage][keyIdx] ?? keys } - constructor(target: T|I18nProxy,setup?:Function) { - this.target = target; - isFunction(setup)&&setup.bind(this)(target); - mixin(this, this.target as object); + constructor(target: T | I18nProxy, setup?: Function) { + this.target = target + isFunction(setup) && setup.bind(this)(target) + mixin(this, this.target as object) } } -const i18nProxy = new I18NextMobxProxy(i18next,defaultSetup) as unknown as I18NextExtendsProxy; +const i18nProxy = new I18NextMobxProxy(i18next, defaultSetup) as unknown as I18NextExtendsProxy const loadLanguage = async (lng: string) => { - let _loadPath:string ="/locales/{{lng}}.json"; - const optionsLoadPath = i18next.options?.backend&&(i18next.options.backend as any).addPath - - if(optionsLoadPath){ - isFunction(optionsLoadPath)?( _loadPath = optionsLoadPath(lng,"translation")):(_loadPath = optionsLoadPath); - } - - return await loadFetch(_loadPath.replace(/\{\{lng\}\}/gi,lng)); -}; - - -function defaultSetup(){ - if (!isServer()) { - i18next.on("languageChanged", function(lng: string) { - if(lng !== i18nProxy.currentLanguage) { - if(!i18next.hasResourceBundle(lng,"translation")){ - loadLanguage(lng).then(data=>{ - i18next.addResourceBundle(lng, 'translation', data) - i18next.changeLanguage(lng).then(()=>{ - i18nProxy.changeCurrentLanguage(i18next.language) - }) - }) - - }else{ - i18nProxy.changeCurrentLanguage(lng) - } - } - }); - } + let _loadPath: string = '/locales/{{lng}}.json' + const optionsLoadPath = i18next.options?.backend && (i18next.options.backend as any).addPath + + if (optionsLoadPath) { + isFunction(optionsLoadPath) ? (_loadPath = optionsLoadPath(lng, 'translation')) : (_loadPath = optionsLoadPath) + } + + return await loadFetch(_loadPath.replace(/\{\{lng\}\}/gi, lng)) } -export { - isServer, - i18next, +function defaultSetup() { + if (!isServer()) { + i18next.on('languageChanged', function (lng: string) { + if (lng !== i18nProxy.currentLanguage) { + if (!i18next.hasResourceBundle(lng, 'translation')) { + loadLanguage(lng).then((data) => { + i18next.addResourceBundle(lng, 'translation', data) + i18next.changeLanguage(lng).then(() => { + i18nProxy.changeCurrentLanguage(i18next.language) + }) + }) + } else { + i18nProxy.changeCurrentLanguage(lng) + } + } + }) + } } -export const t = function ( - key: string | string[], - defaultValue?: string | undefined, - options?: - | string - | TOptions<{ - returnDetails: true; - returnObjects: true; - }> - | undefined, -) { - return i18nProxy.t(key, defaultValue, options); -}; - -export const changeLanguage = i18nProxy.changeLanguage; - -export const currentLanguage = i18nProxy.currentLanguage; - -export default i18nProxy; \ No newline at end of file +export { isServer, i18next } + +export const t = i18nProxy.t + +export const changeLanguage = i18nProxy.changeLanguage + +export const currentLanguage = i18nProxy.currentLanguage + +export default i18nProxy diff --git a/src/next.ts b/src/next.ts index 2785ef6..010f6d9 100644 --- a/src/next.ts +++ b/src/next.ts @@ -1,121 +1,116 @@ -import {i18next,changeLanguage} from "./index"; -import { isExternalUrl, isFunction, isServer, loadFetch, parseLanguageHeader, parseLanguageHeaderCookie } from "./tools"; +import { i18next, changeLanguage } from './index' +import { + isExternalUrl, + isFunction, + isServer, + loadExternalLanguage, + parseLanguageHeader, + parseLanguageHeaderCookie, +} from './tools' interface ProcessI18NextProxy extends NodeJS.Process { - I18NextProxy?: { severLanguage: string }; + I18NextProxy?: { severLanguage: string } } interface I18NextHeaders { - 'accept-language'?: string; - cookie?: string; + 'accept-language'?: string + cookie?: string } - + function parseLanguage(headersData: I18NextHeaders) { - const languages = parseLanguageHeader( - headersData['accept-language'] || 'zh-CN,zh;q=0.9', - ); - const cookieLanguage = parseLanguageHeaderCookie( - headersData.cookie, - ); + const languages = parseLanguageHeader(headersData['accept-language'] || 'zh-CN,zh;q=0.9') + const cookieLanguage = parseLanguageHeaderCookie(headersData.cookie) - return cookieLanguage || languages[0]; + return cookieLanguage || languages[0] } -const loadServerLanguageData = (lng: string,ns:string="translation") => { - return new Promise(resolve => { - let _loadPath:string ="/locales/{{lng}}.json"; - const optionsLoadPath = i18next.options?.backend&&(i18next.options.backend as any).addPath +const loadServerLanguageData = (lng: string, ns: string = 'translation') => { + return new Promise((resolve) => { + let _loadPath: string = '/locales/{{lng}}.json' + const optionsLoadPath = i18next.options?.backend && (i18next.options.backend as any).addPath - if(optionsLoadPath){ - isFunction(optionsLoadPath)?( _loadPath = optionsLoadPath(lng,"translation")):(_loadPath = optionsLoadPath); - } - - if(isExternalUrl(_loadPath)){ - loadFetch(_loadPath).then((data)=>{ - resolve(data); - }) - }else{ - let data = "{}" - try { - const { readFileSync } = require('fs'); - const path = require('path'); - data = readFileSync( - path.join(process.cwd(), 'public', _loadPath.replace(/\{\{lng\}\}/gi,lng)), - 'utf-8', - ); - } catch (error) {} - resolve(JSON.parse(data)); - } - }); -}; + if (optionsLoadPath) { + isFunction(optionsLoadPath) + ? (_loadPath = optionsLoadPath(lng, 'translation')) + : (_loadPath = optionsLoadPath) + } + _loadPath = _loadPath.replace(/\{\{lng\}\}/gi, lng) + if (isExternalUrl(_loadPath)) { + loadExternalLanguage(_loadPath).then((data) => { + resolve(data) + }) + } else { + let data = '{}' + try { + const { readFileSync } = require('fs') + const path = require('path') + data = readFileSync(path.join(process.cwd(), 'public', _loadPath), 'utf-8') + } catch (error) {} + resolve(JSON.parse(data)) + } + }) +} -export const loadServerLanguage = async(lng:string)=>{ - if (lng&&!i18next.hasResourceBundle(lng, 'translation')) { - let data = await loadServerLanguageData(lng) - i18next.addResourceBundle(lng, 'translation',data) - } - await changeLanguage(lng) +export const loadServerLanguage = async (lng: string) => { + if (lng && !i18next.hasResourceBundle(lng, 'translation')) { + let data = await loadServerLanguageData(lng) + i18next.addResourceBundle(lng, 'translation', data) + } + await changeLanguage(lng) } function setI18nConfiguration(HeadersData?: I18NextHeaders) { - if (HeadersData) { - (process as ProcessI18NextProxy).I18NextProxy = { - severLanguage: parseLanguage(HeadersData), - }; - } + if (HeadersData) { + ;(process as ProcessI18NextProxy).I18NextProxy = { + severLanguage: parseLanguage(HeadersData), + } + } } export function getSeverLanguage() { - return (process as ProcessI18NextProxy).I18NextProxy?.severLanguage; + return (process as ProcessI18NextProxy).I18NextProxy?.severLanguage } -export const withNextI18NextMobxApp = (nextAppComponent: { - ({ Component, pageProps }); - getInitialProps?: any; -}) => { - - if (nextAppComponent.getInitialProps) { - nextAppComponent.getInitialProps = (function(_getInitialProps: { - apply: ( - arg0: { - ({ Component, pageProps }); - getInitialProps?: any; - }, - arg1: IArguments, - ) => void; - }) { - return function () { - const arg = arguments; - const HeadersData = arg[0].ctx.req?.headers; - setI18nConfiguration(HeadersData); - arg[0].ctx.query.i18nextLanguage = i18next.options.lng || getSeverLanguage(); - - return new Promise(resolve => { - if(isServer()){ - loadServerLanguage(arg[0].ctx.query.i18nextLanguage||i18next.language).then(()=>{ - resolve(_getInitialProps.apply(nextAppComponent, arg)); - }) - }else{ - resolve(_getInitialProps.apply(nextAppComponent, arg)); +export const withNextI18NextMobxApp = (nextAppComponent: { ({ Component, pageProps }); getInitialProps?: any }) => { + if (nextAppComponent.getInitialProps) { + nextAppComponent.getInitialProps = (function (_getInitialProps: { + apply: ( + arg0: { + ({ Component, pageProps }) + getInitialProps?: any + }, + arg1: IArguments, + ) => void + }) { + return function () { + const arg = arguments + const HeadersData = arg[0].ctx.req?.headers + setI18nConfiguration(HeadersData) + arg[0].ctx.query.i18nextLanguage = i18next.options.lng || getSeverLanguage() + + return new Promise((resolve) => { + if (isServer()) { + loadServerLanguage(arg[0].ctx.query.i18nextLanguage || i18next.language).then(() => { + resolve(_getInitialProps.apply(nextAppComponent, arg)) + }) + } else { + resolve(_getInitialProps.apply(nextAppComponent, arg)) + } + }) + } + })(nextAppComponent.getInitialProps) + } else { + nextAppComponent.getInitialProps = async ({ Component, ctx }) => { + const HeadersData = ctx.req?.headers + setI18nConfiguration(HeadersData) + ctx.query.i18nextLanguage = i18next.options.lng || getSeverLanguage() + if (isServer()) { + await loadServerLanguage(ctx.query.i18nextLanguage || i18next.language) + } + let pageProps = {} + if (Component.getInitialProps) { + pageProps = await Component.getInitialProps(ctx) + } + return { pageProps } } - }); - }; - })(nextAppComponent.getInitialProps); - } else { - nextAppComponent.getInitialProps = async ({ - Component, - ctx, - }) => { - const HeadersData = ctx.req?.headers; - setI18nConfiguration(HeadersData); - ctx.query.i18nextLanguage = i18next.options.lng || getSeverLanguage(); - if(isServer()){ - await loadServerLanguage(ctx.query.i18nextLanguage || i18next.language) - } - let pageProps = {}; - if (Component.getInitialProps) { - pageProps = await Component.getInitialProps(ctx); - } - return { pageProps }; - }; - } + } - return nextAppComponent; -}; + return nextAppComponent +} diff --git a/src/tools.ts b/src/tools.ts index 1a170c3..c6f6eab 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -1,44 +1,69 @@ +import * as https from 'https' +import * as http from 'http' -export const isServer = () => typeof window === 'undefined'; +export const isServer = () => typeof window === 'undefined' -export const isFunction = value =>Object.prototype.toString.call(value) === '[object Function]' +export const isFunction = (value) => Object.prototype.toString.call(value) === '[object Function]' -export const isExternalUrl = value =>/^(https?:){0,1}\/\/\S/.test(value) +export const isExternalUrl = (value) => /^(https?:){0,1}\/\/\S/.test(value.trim()) -export const loadFetch = async value => await fetch(value).then(res => res.json()); +export const loadFetch = async (value) => await fetch(value.trim()).then((res) => res.json()) +export const loadExternalLanguage = (value) => + new Promise((resolve) => { + let protocol = new URL(value).protocol == 'https:' ? https : http + protocol + .get(value, (res) => { + let list = [] + res.on('data', (chunk) => { + list.push(chunk) + }) + res.on('end', () => { + let data = {} + try { + data = JSON.parse(Buffer.concat(list).toString()) + } catch (error) { + console.error('language path does not exist') + } + resolve(data) + }) + }) + .on('error', (err) => { + console.log('Error: ', err.message) + }) + }) export function mixin(target: object, sources: object) { - for (const key of Object.getOwnPropertyNames(sources||{})) { - if (!target[key]) { - target[key] = sources[key]; - } + for (const key of Object.getOwnPropertyNames(sources || {})) { + if (!target[key]) { + target[key] = sources[key] + } } } function parseCookie(cookie: string) { - let pattern = /([^=]+)=([^;]+);?\s*/g, - result, - value: Record = {}; - while ((result = pattern.exec(cookie)) != null) { - value[result[1]] = result[2]; - } - return value; + let pattern = /([^=]+)=([^;]+);?\s*/g, + result: string[], + value: Record = {} + while ((result = pattern.exec(cookie)) != null) { + value[result[1]] = result[2] + } + return value } export const parseLanguageHeaderCookie = (value: string, name: string = 'i18next') => { - let result = parseCookie(value); - return result[name] ? result[name] : ''; -}; + let result = parseCookie(value) + return result[name] ? result[name] : '' +} export const parseLanguageHeader = (value: string) => - value - .split(',') - .map(language => { - const [name, quantity = ''] = language.split(';'); - const [_, value = '1'] = quantity.split('='); + value + .split(',') + .map((language) => { + const [name, quantity = ''] = language.split(';') + const [_, value = '1'] = quantity.split('=') - return [name.trim(), +value] as const; - }) - .sort(([_, a], [__, b]) => b - a) - .map(([name]) => name); \ No newline at end of file + return [name.trim(), +value] as const + }) + .sort(([_, a], [__, b]) => b - a) + .map(([name]) => name)