diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4966780..b12f6aa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,3 +40,5 @@ jobs: run: pnpm install - name: Test run: pnpm test + # https://github.com/actions/runner/issues/795 + - run: echo "NODE_ICU_DATA=" >> $GITHUB_ENV diff --git a/limitation.md b/limitation.md new file mode 100644 index 0000000..c3ef014 --- /dev/null +++ b/limitation.md @@ -0,0 +1,5 @@ +# Limitations +- `createI18n` is singleton instance + +# TODOs +- postTranslation I/F miss-match (cannot pass the key) diff --git a/package.json b/package.json index 172b02c..89404fa 100644 --- a/package.json +++ b/package.json @@ -22,15 +22,19 @@ }, "devDependencies": { "@antfu/eslint-config-ts": "^0.6.6", + "@intlify/vue-i18n-core": "^9.2.0-beta.5", "@kazupon/lerna-changelog": "^4.3.0", "@secretlint/secretlint-rule-preset-recommend": "^3.1.0", "@types/jest": "^27.0.1", "@vue/composition-api": "^1.1.4", "@vue/test-utils": "^1.2.2", "@vue/vue2-jest": "^27.0.0-alpha.1", + "babel-jest": "^27.2.0", + "cross-env": "^7.0.3", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^4.0.0", + "full-icu": "^1.3.1", "jest": "^27.1.1", "lint-staged": "^11.1.2", "npm-run-all": "^4.1.5", @@ -105,8 +109,11 @@ "release:trigger": "shipjs trigger", "test": "npm run test:cover", "test:cover": "npm run test:unit -- --coverage", - "test:unit": "jest --clearCache && jest --env node" + "test:unit": "jest --clearCache && cross-env NODE_ICU_DATA=./node_modules/full-icu jest --env node" }, "sideEffects": false, - "types": "dist/index.d.ts" + "types": "dist/index.d.ts", + "dependencies": { + "@intlify/shared": "^9.2.0-beta.4" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 97f1715..793098e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,15 +5,20 @@ importers: .: specifiers: '@antfu/eslint-config-ts': ^0.6.6 + '@intlify/shared': ^9.2.0-beta.4 + '@intlify/vue-i18n-core': ^9.2.0-beta.5 '@kazupon/lerna-changelog': ^4.3.0 '@secretlint/secretlint-rule-preset-recommend': ^3.1.0 '@types/jest': ^27.0.1 '@vue/composition-api': ^1.1.4 '@vue/test-utils': ^1.2.2 '@vue/vue2-jest': ^27.0.0-alpha.1 + babel-jest: ^27.2.0 + cross-env: ^7.0.3 eslint: ^7.32.0 eslint-config-prettier: ^8.3.0 eslint-plugin-prettier: ^4.0.0 + full-icu: ^1.3.1 jest: ^27.1.1 lint-staged: ^11.1.2 npm-run-all: ^4.1.5 @@ -26,24 +31,30 @@ importers: vue: ^2.6.12 vue-i18n: ^8.25.0 yorkie: ^2.0.0 + dependencies: + '@intlify/shared': 9.2.0-beta.4 devDependencies: '@antfu/eslint-config-ts': 0.6.6_eslint@7.32.0+typescript@4.4.2 + '@intlify/vue-i18n-core': 9.2.0-beta.5_vue@2.6.14 '@kazupon/lerna-changelog': 4.3.0 '@secretlint/secretlint-rule-preset-recommend': 3.3.0 '@types/jest': 27.0.1 '@vue/composition-api': 1.1.4_vue@2.6.14 '@vue/test-utils': 1.2.2_vue@2.6.14 - '@vue/vue2-jest': 27.0.0-alpha.1_1c0645fdae857e3092c13ae706abaaf0 + '@vue/vue2-jest': 27.0.0-alpha.1_fc57e9a142e6bd141da0b929967b49dc + babel-jest: 27.2.0 + cross-env: 7.0.3 eslint: 7.32.0 eslint-config-prettier: 8.3.0_eslint@7.32.0 eslint-plugin-prettier: 4.0.0_5559632aa3c77deec3ae5c2ea6ed0f36 + full-icu: 1.3.4 jest: 27.1.1 lint-staged: 11.1.2 npm-run-all: 4.1.5 prettier: 2.4.0 secretlint: 3.3.0 shipjs: 0.23.3 - ts-jest: 27.0.5_0446e0f5b8a8e6e03ce64cc79c44391d + ts-jest: 27.0.5_2c38a5ee211c725f711c6e0cf133e6ba tsup: 4.14.0_typescript@4.4.2 typescript: 4.4.2 vue: 2.6.14 @@ -319,6 +330,14 @@ packages: hasBin: true dev: true + /@babel/plugin-syntax-async-generators/7.8.4: + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + dev: true + /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.15.5: resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: @@ -328,6 +347,14 @@ packages: '@babel/helper-plugin-utils': 7.14.5 dev: true + /@babel/plugin-syntax-bigint/7.8.3: + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + dev: true + /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.15.5: resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: @@ -337,6 +364,14 @@ packages: '@babel/helper-plugin-utils': 7.14.5 dev: true + /@babel/plugin-syntax-class-properties/7.12.13: + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + dev: true + /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.15.5: resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: @@ -346,6 +381,14 @@ packages: '@babel/helper-plugin-utils': 7.14.5 dev: true + /@babel/plugin-syntax-import-meta/7.10.4: + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + dev: true + /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.15.5: resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: @@ -355,6 +398,14 @@ packages: '@babel/helper-plugin-utils': 7.14.5 dev: true + /@babel/plugin-syntax-json-strings/7.8.3: + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + dev: true + /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.15.5: resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: @@ -364,6 +415,14 @@ packages: '@babel/helper-plugin-utils': 7.14.5 dev: true + /@babel/plugin-syntax-logical-assignment-operators/7.10.4: + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + dev: true + /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.15.5: resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: @@ -373,6 +432,14 @@ packages: '@babel/helper-plugin-utils': 7.14.5 dev: true + /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3: + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + dev: true + /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.15.5: resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: @@ -382,6 +449,14 @@ packages: '@babel/helper-plugin-utils': 7.14.5 dev: true + /@babel/plugin-syntax-numeric-separator/7.10.4: + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + dev: true + /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.15.5: resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: @@ -391,6 +466,14 @@ packages: '@babel/helper-plugin-utils': 7.14.5 dev: true + /@babel/plugin-syntax-object-rest-spread/7.8.3: + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + dev: true + /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.15.5: resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: @@ -400,6 +483,14 @@ packages: '@babel/helper-plugin-utils': 7.14.5 dev: true + /@babel/plugin-syntax-optional-catch-binding/7.8.3: + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + dev: true + /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.15.5: resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: @@ -409,6 +500,14 @@ packages: '@babel/helper-plugin-utils': 7.14.5 dev: true + /@babel/plugin-syntax-optional-chaining/7.8.3: + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + dev: true + /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.15.5: resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: @@ -418,6 +517,15 @@ packages: '@babel/helper-plugin-utils': 7.14.5 dev: true + /@babel/plugin-syntax-top-level-await/7.14.5: + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + dev: true + /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.15.5: resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} @@ -577,6 +685,62 @@ packages: webpack: 4.46.0 dev: true + /@intlify/core-base/9.2.0-beta.5: + resolution: {integrity: sha512-GwYu3lKKglr2/k++U+tgtVxtH3PmUZ7O3qoAiuHAw4d8WnfCmHdSroIrC7+V/sRqDzcEbwFnlBj6wpUYwlI3jQ==} + engines: {node: '>= 12'} + dependencies: + '@intlify/devtools-if': 9.2.0-beta.5 + '@intlify/message-compiler': 9.2.0-beta.5 + '@intlify/shared': 9.2.0-beta.5 + '@intlify/vue-devtools': 9.2.0-beta.5 + dev: true + + /@intlify/devtools-if/9.2.0-beta.5: + resolution: {integrity: sha512-rTg2a5lZI0P2U829MX+RlYwTHVcJv2jrFBS5wCo+4AU8akVYxLaSWFU+CWGL82XdhuRUVylknG8tlyTGWX1RaA==} + engines: {node: '>= 12'} + dependencies: + '@intlify/shared': 9.2.0-beta.5 + dev: true + + /@intlify/message-compiler/9.2.0-beta.5: + resolution: {integrity: sha512-1nc+qWi+dObEN8mXq0a2R6fTPydES1hUSuZf+IBBpsretSIaNGcILE1gTB2te1pgb39TJEYBuesaOutYh60EFg==} + engines: {node: '>= 12'} + dependencies: + '@intlify/shared': 9.2.0-beta.5 + source-map: 0.6.1 + dev: true + + /@intlify/shared/9.2.0-beta.4: + resolution: {integrity: sha512-Qe1QB4y594PjAOS3hWjK0tMGmpLPHb7ZksgR+u1wgpdVMrdL5er0vuPa1HGHF44ino1LK+Cbc0gdcWbgNutteg==} + engines: {node: '>= 12'} + dev: false + + /@intlify/shared/9.2.0-beta.5: + resolution: {integrity: sha512-GDiiOZYXRLZPsrT33mqtdqi2YrUliZMqKX0F90MPigbnaaOK4yv/Qm24SJSPmxAASWc/2H4KmMpOaqv/yCPXcA==} + engines: {node: '>= 12'} + dev: true + + /@intlify/vue-devtools/9.2.0-beta.5: + resolution: {integrity: sha512-+pAwyg096UqQDCrSxR9cRzUdL3h12ZnHKaAqN0TQz0UIYy+DTSGpvzrD7EM8AOeS5K3gSvKIyir7xk4rzLSp9w==} + engines: {node: '>= 12'} + dependencies: + '@intlify/core-base': 9.2.0-beta.5 + '@intlify/shared': 9.2.0-beta.5 + dev: true + + /@intlify/vue-i18n-core/9.2.0-beta.5_vue@2.6.14: + resolution: {integrity: sha512-UiF2wO9/4X0qswJds1dFgWq+gp815TnlkFPOtQKL17GfXKToD0XS5ORhc5MBRPjKOQIow28GnGRk/5uU2bI5AA==} + engines: {node: '>= 12'} + peerDependencies: + vue: ^3.0.0 + dependencies: + '@intlify/core-base': 9.2.0-beta.5 + '@intlify/shared': 9.2.0-beta.5 + '@intlify/vue-devtools': 9.2.0-beta.5 + '@vue/devtools-api': 6.0.0-beta.15 + vue: 2.6.14 + dev: true + /@istanbuljs/load-nyc-config/1.1.0: resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} @@ -773,6 +937,29 @@ packages: - supports-color dev: true + /@jest/transform/27.2.0: + resolution: {integrity: sha512-Q8Q/8xXIZYllk1AF7Ou5sV3egOZsdY/Wlv09CSbcexBRcC1Qt6lVZ7jRFAZtbHsEEzvOCyFEC4PcrwKwyjXtCg==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@babel/core': 7.15.5 + '@jest/types': 27.1.1 + babel-plugin-istanbul: 6.0.0 + chalk: 4.1.2 + convert-source-map: 1.8.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.8 + jest-haste-map: 27.2.0 + jest-regex-util: 27.0.6 + jest-util: 27.2.0 + micromatch: 4.0.4 + pirates: 4.0.1 + slash: 3.0.0 + source-map: 0.6.1 + write-file-atomic: 3.0.3 + transitivePeerDependencies: + - supports-color + dev: true + /@jest/types/27.1.1: resolution: {integrity: sha512-yqJPDDseb0mXgKqmNqypCsb85C22K1aY5+LUxh7syIM9n/b0AsaltxNy+o6tt29VcfGDpYEve175bm3uOhcehA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -1677,6 +1864,10 @@ packages: tslib: 2.3.1 vue: 2.6.14 + /@vue/devtools-api/6.0.0-beta.15: + resolution: {integrity: sha512-quBx4Jjpexo6KDiNUGFr/zF/2A4srKM9S9v2uHgMXSU//hjgq1eGzqkIFql8T9gfX5ZaVOUzYBP3jIdIR3PKIA==} + dev: true + /@vue/preload-webpack-plugin/1.1.2_502c618fc8a7d35df07e93275324a2d0: resolution: {integrity: sha512-LIZMuJk38pk9U9Ur4YzHjlIyMuxPlACdBIHH9/nGYVTsaGKOSnSuELiE8vS9wa+dJpIYspYUOqk+L1Q4pgHQHQ==} engines: {node: '>=6.0.0'} @@ -1700,7 +1891,7 @@ packages: vue: 2.6.14 dev: true - /@vue/vue2-jest/27.0.0-alpha.1_1c0645fdae857e3092c13ae706abaaf0: + /@vue/vue2-jest/27.0.0-alpha.1_fc57e9a142e6bd141da0b929967b49dc: resolution: {integrity: sha512-dz0+niq+25E6p6o6CRPi4blM4UH+GN3z4H1TfSQZQVmUgiEUE1AX28C5xt2cnzoxpKX4RWZzBjliMsUq/5J1+w==} peerDependencies: '@babel/core': 7.x @@ -1715,11 +1906,12 @@ packages: dependencies: '@babel/plugin-transform-modules-commonjs': 7.15.4 '@vue/component-compiler-utils': 3.2.2 + babel-jest: 27.2.0 chalk: 2.4.2 extract-from-css: 0.4.4 jest: 27.1.1 source-map: 0.5.6 - ts-jest: 27.0.5_0446e0f5b8a8e6e03ce64cc79c44391d + ts-jest: 27.0.5_2c38a5ee211c725f711c6e0cf133e6ba vue: 2.6.14 transitivePeerDependencies: - supports-color @@ -2319,6 +2511,24 @@ packages: - supports-color dev: true + /babel-jest/27.2.0: + resolution: {integrity: sha512-bS2p+KGGVVmWXBa8+i6SO/xzpiz2Q/2LnqLbQknPKefWXVZ67YIjA4iXup/jMOEZplga9PpWn+wrdb3UdDwRaA==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + dependencies: + '@jest/transform': 27.2.0 + '@jest/types': 27.1.1 + '@types/babel__core': 7.1.16 + babel-plugin-istanbul: 6.0.0 + babel-preset-jest: 27.2.0 + chalk: 4.1.2 + graceful-fs: 4.2.8 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + /babel-plugin-dynamic-import-node/2.3.3: resolution: {integrity: sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==} dependencies: @@ -2348,6 +2558,35 @@ packages: '@types/babel__traverse': 7.14.2 dev: true + /babel-plugin-jest-hoist/27.2.0: + resolution: {integrity: sha512-TOux9khNKdi64mW+0OIhcmbAn75tTlzKhxmiNXevQaPbrBYK7YKjP1jl6NHTJ6XR5UgUrJbCnWlKVnJn29dfjw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@babel/template': 7.15.4 + '@babel/types': 7.15.4 + '@types/babel__core': 7.1.16 + '@types/babel__traverse': 7.14.2 + dev: true + + /babel-preset-current-node-syntax/1.0.1: + resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/plugin-syntax-async-generators': 7.8.4 + '@babel/plugin-syntax-bigint': 7.8.3 + '@babel/plugin-syntax-class-properties': 7.12.13 + '@babel/plugin-syntax-import-meta': 7.10.4 + '@babel/plugin-syntax-json-strings': 7.8.3 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3 + '@babel/plugin-syntax-numeric-separator': 7.10.4 + '@babel/plugin-syntax-object-rest-spread': 7.8.3 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3 + '@babel/plugin-syntax-optional-chaining': 7.8.3 + '@babel/plugin-syntax-top-level-await': 7.14.5 + dev: true + /babel-preset-current-node-syntax/1.0.1_@babel+core@7.15.5: resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} peerDependencies: @@ -2379,6 +2618,16 @@ packages: babel-preset-current-node-syntax: 1.0.1_@babel+core@7.15.5 dev: true + /babel-preset-jest/27.2.0: + resolution: {integrity: sha512-z7MgQ3peBwN5L5aCqBKnF6iqdlvZvFUQynEhu0J+X9nHLU72jO3iY331lcYrg+AssJ8q7xsv5/3AICzVmJ/wvg==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + babel-plugin-jest-hoist: 27.2.0 + babel-preset-current-node-syntax: 1.0.1 + dev: true + /balanced-match/1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true @@ -3482,6 +3731,14 @@ packages: sha.js: 2.4.11 dev: true + /cross-env/7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + dependencies: + cross-spawn: 7.0.3 + dev: true + /cross-spawn/5.1.0: resolution: {integrity: sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=} dependencies: @@ -5181,6 +5438,12 @@ packages: dev: true optional: true + /full-icu/1.3.4: + resolution: {integrity: sha512-BERy9j2ybYSfP8QmXyg496NjVrGXfM73TZckQ5s5hgDV2lpKshjGfPEYYWU3hhE2kU8atZXUZNLSeNz4OQ8hNA==} + hasBin: true + requiresBuild: true + dev: true + /function-bind/1.1.1: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true @@ -6695,6 +6958,26 @@ packages: fsevents: 2.3.2 dev: true + /jest-haste-map/27.2.0: + resolution: {integrity: sha512-laFet7QkNlWjwZtMGHCucLvF8o9PAh2cgePRck1+uadSM4E4XH9J4gnx4do+a6do8ZV5XHNEAXEkIoNg5XUH2Q==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/types': 27.1.1 + '@types/graceful-fs': 4.1.5 + '@types/node': 16.9.0 + anymatch: 3.1.2 + fb-watchman: 2.0.1 + graceful-fs: 4.2.8 + jest-regex-util: 27.0.6 + jest-serializer: 27.0.6 + jest-util: 27.2.0 + jest-worker: 27.2.0 + micromatch: 4.0.4 + walker: 1.0.7 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /jest-jasmine2/27.1.1: resolution: {integrity: sha512-0LAzUmcmvQwjIdJt0cXUVX4G5qjVXE8ELt6nbMNDzv2yAs2hYCCUtQq+Eje70GwAysWCGcS64QeYj5VPHYVxPg==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -6926,6 +7209,18 @@ packages: picomatch: 2.3.0 dev: true + /jest-util/27.2.0: + resolution: {integrity: sha512-T5ZJCNeFpqcLBpx+Hl9r9KoxBCUqeWlJ1Htli+vryigZVJ1vuLB9j35grEBASp4R13KFkV7jM52bBGnArpJN6A==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + '@jest/types': 27.1.1 + '@types/node': 16.9.0 + chalk: 4.1.2 + graceful-fs: 4.2.8 + is-ci: 3.0.0 + picomatch: 2.3.0 + dev: true + /jest-validate/27.1.1: resolution: {integrity: sha512-N5Er5FKav/8m2dJwn7BGnZwnoD1BSc8jx5T+diG2OvyeugvZDhPeAt5DrNaGkkaKCrSUvuE7A5E4uHyT7Vj0Mw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -6960,6 +7255,15 @@ packages: supports-color: 8.1.1 dev: true + /jest-worker/27.2.0: + resolution: {integrity: sha512-laB0ZVIBz+voh/QQy9dmUuuDsadixeerrKqyVpgPz+CCWiOYjOBabUXHIXZhsdvkWbLqSHbgkAHWl5cg24Q6RA==} + engines: {node: '>= 10.13.0'} + dependencies: + '@types/node': 16.9.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + /jest/27.1.1: resolution: {integrity: sha512-LFTEZOhoZNR/2DQM3OCaK5xC6c55c1OWhYh0njRsoHX0qd6x4nkcgenkSH0JKjsAGMTmmJAoL7/oqYHMfwhruA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -10819,7 +11123,7 @@ packages: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} dev: true - /ts-jest/27.0.5_0446e0f5b8a8e6e03ce64cc79c44391d: + /ts-jest/27.0.5_2c38a5ee211c725f711c6e0cf133e6ba: resolution: {integrity: sha512-lIJApzfTaSSbtlksfFNHkWOzLJuuSm4faFAfo5kvzOiRAuoN4/eKxVJ2zEAho8aecE04qX6K1pAzfH5QHL1/8w==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} hasBin: true @@ -10838,6 +11142,7 @@ packages: optional: true dependencies: '@types/jest': 27.0.1 + babel-jest: 27.2.0 bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 jest: 27.1.1 diff --git a/ship.config.js b/ship.config.js index e1ca58a..ce02a68 100644 --- a/ship.config.js +++ b/ship.config.js @@ -39,7 +39,7 @@ module.exports = { mergeStrategy: { toSameBranch: ['master'] }, monorepo: undefined, updateChangelog: false, - buildCommand: ({ isYarn }) => 'npm run build', + buildCommand: ({ isYarn }) => 'pnpm run build', beforeCommitChanges: ({ nextVersion, exec, dir }) => { return new Promise(resolve => { const pkg = require('./package.json') diff --git a/src/composer.ts b/src/composer.ts new file mode 100644 index 0000000..ef23e71 --- /dev/null +++ b/src/composer.ts @@ -0,0 +1,313 @@ +import { computed, watch } from '@vue/composition-api' +import VueI18n from 'vue-i18n' +import { + isBoolean, + isPlainObject, + isFunction, + isArray, + isObject, + isString, + isRegExp, + hasOwn +} from '@intlify/shared' +import type { + Locale, + FallbackLocale, + VueMessageType, + LocaleMessages, + LocaleMessage, + IntlDateTimeFormats as DateTimeFormatsType, + IntlNumberFormats as NumberFormatsType, + Composer, + CustomBlocks +} from '@intlify/vue-i18n-core' +import type { I18nOptions } from 'vue-i18n' + +const DEFAULT_LOCALE = 'en-US' +let composerID = 0 + +export interface ComposerInternalOptions< + Messages = {}, + DateTimeFormats = {}, + NumberFormats = {} +> { + __i18n?: CustomBlocks + __i18nGlobal?: CustomBlocks + __root?: Composer + __rootVM?: any +} + +type GetLocaleMessagesOptions = { + messages?: { [K in keyof Messages]: Messages[K] } + __i18n?: CustomBlocks + // messageResolver?: MessageResolver + // flatJson?: boolean +} + +export function getLocaleMessages( + locale: Locale, + options: GetLocaleMessagesOptions +): { [K in keyof Messages]: Messages[K] } { + const { messages, __i18n } = options + + // prettier-ignore + const ret = isPlainObject(messages) + ? messages + : isArray(__i18n) + ? {} + : { [locale]: {} } + + // merge locale messages of i18n custom block + if (isArray(__i18n)) { + __i18n.forEach(({ locale, resource }) => { + if (locale) { + ret[locale] = ret[locale] || {} + deepCopy(resource, ret[locale]) + } else { + deepCopy(resource, ret) + } + }) + } + + return ret as { [K in keyof Messages]: Messages[K] } +} + +const isNotObjectOrIsArray = (val: unknown) => !isObject(val) || isArray(val) + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function deepCopy(src: any, des: any): void { + // src and des should both be objects, and non of then can be a array + if (isNotObjectOrIsArray(src) || isNotObjectOrIsArray(des)) { + // TODO + throw new Error('ss') + } + + // eslint-disable-next-line no-restricted-syntax + for (const key in src) { + if (hasOwn(src, key)) { + if (isNotObjectOrIsArray(src[key]) || isNotObjectOrIsArray(des[key])) { + // replace with src[key] when: + // src[key] or des[key] is not a object, or + // src[key] or des[key] is a array + des[key] = src[key] + } else { + // src[key] and des[key] are both object, merge them + deepCopy(src[key], des[key]) + } + } + } +} + +export function createComposer(options: any = {}): any { + type Message = VueMessageType + const { __root, __rootVM } = options as ComposerInternalOptions< + LocaleMessages>, + DateTimeFormatsType, + NumberFormatsType + > + // TODO: const _isGlobal = __root === undefined + + const _inheritLocale = isBoolean(options.inheritLocale) + ? options.inheritLocale + : true + + // prettier-ignore + const _locale = __root && _inheritLocale + ? __root.locale.value + : isString(options.locale) + ? options.locale + : DEFAULT_LOCALE + + // prettier-ignore + const _fallbackLocale = + __root && _inheritLocale + ? __root.fallbackLocale.value + : isString(options.fallbackLocale) || + isArray(options.fallbackLocale) || + isPlainObject(options.fallbackLocale) || + options.fallbackLocale === false + ? options.fallbackLocale + : _locale + + const _messages = getLocaleMessages>>( + _locale, + options + ) + + // prettier-ignore + const _datetimeFormats = isPlainObject(options.datetimeFormats) + ? options.datetimeFormats + : { [_locale]: {} } + + // prettier-ignore + const _numberFormats = isPlainObject(options.numberFormats) + ? options.numberFormats + : { [_locale]: {} } + + // prettier-ignore + const _missingWarn = __root + ? __root.missingWarn + : isBoolean(options.missingWarn) || isRegExp(options.missingWarn) + ? options.missingWarn + : true + + // prettier-ignore + const _fallbackWarn = __root + ? __root.fallbackWarn + : isBoolean(options.fallbackWarn) || isRegExp(options.fallbackWarn) + ? options.fallbackWarn + : true + + // prettier-ignore + const _fallbackRoot = __root + ? __root.fallbackRoot + : isBoolean(options.fallbackRoot) + ? options.fallbackRoot + : true + + const _fallbackFormat = !!options.fallbackFormat + + const _missing = isFunction(options.missing) ? options.missing : null + + const _postTranslation = isFunction(options.postTranslation) + ? options.postTranslation + : null + + const _warnHtmlMessage = isBoolean(options.warnHtmlMessage) + ? options.warnHtmlMessage + : true + + const _escapeParameter = !!options.escapeParameter + + // prettier-ignore + const _modifiers = __root + ? __root.modifiers + : isPlainObject(options.modifiers) + ? options.modifiers + : {} + + const _pluralRules = options.pluralRules || (__root && __root.pluralRules) + + const legacyOptions: I18nOptions = { + locale: _locale, + fallbackLocale: _fallbackLocale, + messages: _messages, + dateTimeFormats: _datetimeFormats, + numberFormats: _numberFormats, + modifiers: _modifiers, + missing: _missing, + fallbackRoot: _fallbackRoot, + postTranslation: _postTranslation, + pluralizationRules: _pluralRules, + escapeParameterHtml: _escapeParameter, + sync: _inheritLocale, + silentFallbackWarn: isBoolean(_fallbackWarn) + ? !_fallbackWarn + : _fallbackWarn, + silentTranslationWarn: isBoolean(_missingWarn) + ? !_missingWarn + : _missingWarn, + formatFallbackMessages: isBoolean(_fallbackFormat) + ? !_fallbackFormat + : _fallbackFormat, + warnHtmlInMessage: isBoolean(_warnHtmlMessage) + ? _warnHtmlMessage + ? 'warn' + : 'off' + : 'off' + } + if (__rootVM) { + ;(legacyOptions as any).root = __rootVM + } + + const _legacy = new VueI18n(legacyOptions) + + // track reactivity + /* + function trackReactivityValues() { + // TODO: + return [ + _legacy.locale, + _legacy.fallbackLocale, + _legacy.messages, + _legacy.dateTimeFormats, + _legacy.numberFormats + ] + } + */ + + // locale + const locale = computed({ + get: () => _legacy.locale, + set: val => { + _legacy.locale = val + } + }) + + // fallbackLocale + const fallbackLocale = computed({ + get: () => _legacy.fallbackLocale, + set: val => { + _legacy.fallbackLocale = val + } + }) + + // messages + const messages = computed, Message>>( + () => _legacy.messages + ) + + // datetimeFormats + const datetimeFormats = computed( + () => _legacy.dateTimeFormats + ) + + // numberFormats + const numberFormats = computed(() => _legacy.numberFormats) + + function t(...args: unknown[]): string { + return Reflect.apply(_legacy.t, _legacy, [...args]) as string + } + + // for debug + composerID++ + + // watch root locale & fallbackLocale + if (__root) { + watch(__root.locale, (val: Locale) => { + if (_inheritLocale) { + _legacy.locale = val + } + }) + watch(__root.fallbackLocale, (val: FallbackLocale) => { + if (_inheritLocale) { + _legacy.fallbackLocale = val + } + }) + } + + const composer = { + id: composerID, + locale, + fallbackLocale, + get inheritLocale(): boolean { + return _inheritLocale + }, + set inheritLocale(val: boolean) { + ;(_legacy as any)._sync = val + if (val && __root) { + _legacy.locale = __root.locale.value as Locale + _legacy.fallbackLocale = __root.fallbackLocale.value + } + }, + get availableLocales(): Locale[] { + return Object.keys(_legacy.messages).sort() + }, + messages, + datetimeFormats, + numberFormats, + t + } + + return composer +} diff --git a/src/index.ts b/src/index.ts index fde8e2a..b0dfca4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,52 +1,240 @@ -import Vue from 'vue' import VueI18n from 'vue-i18n' import { computed, getCurrentInstance } from '@vue/composition-api' -import { VueConstructor } from 'vue/types/umd' -import type { WritableComputedRef } from '@vue/composition-api' +import { isBoolean, isObject, isEmptyObject, assign } from '@intlify/shared' +import type { ComponentInternalInstance } from '@vue/composition-api' +import type { + I18nOptions, + ComposerOptions, + Locale, + I18n, + I18nMode, + I18nScope, + UseI18nOptions, + Composer, + VueI18n as LegacyVueI18n, + LocaleParams, + SchemaParams, + DefaultLocaleMessageSchema, + VueMessageType +} from '@intlify/vue-i18n-core' +import { getLocaleMessages } from './composer' -let i18nInstance: VueI18n | undefined +declare module 'vue/types/vue' { + interface Vue { + readonly $i18n: VueI18n & I18n + } +} + +export interface I18nInternal< + Messages = {}, + DateTimeFormats = {}, + NumberFormats = {}, + OptionLocale = Locale +> { + __instances: Map< + ComponentInternalInstance, + | LegacyVueI18n + | Composer + > + __getInstance< + Instance extends + | LegacyVueI18n + | Composer + >( + component: ComponentInternalInstance + ): Instance | null + __setInstance< + Instance extends + | LegacyVueI18n + | Composer + >( + component: ComponentInternalInstance, + instance: Instance + ): void + __deleteInstance(component: ComponentInternalInstance): void +} + +export function createI18n(options: I18nOptions = {}): VueI18n & I18n { + const legacyMode = isBoolean(options.legacy) ? options.legacy : true + const i18nMode: I18nMode = legacyMode ? 'legacy' : 'composition' + + const i18n = new VueI18n( + convertToLegacyI18nOptions(options, i18nMode) + ) as VueI18n & I18n -export function createI18n(options?: VueI18n.I18nOptions): VueI18n { - i18nInstance = new VueI18n(options) + const _i18n = i18n as any + _i18n.mode = i18nMode + _i18n.global = _i18n - return i18nInstance + return i18n } -export interface Composer { - locale: WritableComputedRef - t: typeof VueI18n.prototype.t - tc: typeof VueI18n.prototype.tc - te: typeof VueI18n.prototype.te - d: typeof VueI18n.prototype.d - n: typeof VueI18n.prototype.n +function convertToLegacyI18nOptions( + options: I18nOptions, + mode: I18nMode +): VueI18n.I18nOptions { + let legacyOptions: VueI18n.I18nOptions = {} + legacyOptions.locale = options.locale + legacyOptions.fallbackLocale = options.fallbackLocale + legacyOptions.messages = options.messages + legacyOptions.dateTimeFormats = options.datetimeFormats // from vue-i18n@v9.x + legacyOptions.numberFormats = options.numberFormats + legacyOptions.missing = options.missing + legacyOptions.modifiers = options.modifiers as VueI18n.Modifiers + legacyOptions.fallbackRoot = options.fallbackRoot + + if (mode === 'legacy') { + legacyOptions = assign(legacyOptions, options) + } else { + // convert from composer options + const composerOptions = options as ComposerOptions + legacyOptions.sync = composerOptions.inheritLocale + + legacyOptions.silentFallbackWarn = isBoolean(composerOptions.fallbackWarn) + ? !composerOptions.fallbackWarn + : composerOptions.fallbackWarn + legacyOptions.silentTranslationWarn = isBoolean(composerOptions.missingWarn) + ? !composerOptions.missingWarn + : composerOptions.missingWarn + // prettier-ignore + legacyOptions.formatFallbackMessages = isBoolean(composerOptions.fallbackFormat) + ? !composerOptions.fallbackFormat + : composerOptions.fallbackFormat + legacyOptions.pluralizationRules = composerOptions.pluralRules + legacyOptions.warnHtmlInMessage = isBoolean(composerOptions.warnHtmlMessage) + ? composerOptions.warnHtmlMessage + ? 'warn' + : 'off' + : 'off' + legacyOptions.postTranslation = + composerOptions.postTranslation as VueI18n.PostTranslationHandler + legacyOptions.escapeParameterHtml = composerOptions.escapeParameter + } + + return legacyOptions } -export function useI18n(): Composer { - if (!i18nInstance) throw new Error('vue-i18n not initialized') +export function useI18n( + options?: Options +): Composer< + NonNullable, + NonNullable, + NonNullable, + NonNullable +> - const i18n = i18nInstance +export function useI18n< + Schema = DefaultLocaleMessageSchema, + Locales = 'en-US', + Options extends UseI18nOptions< + SchemaParams, + LocaleParams + > = UseI18nOptions< + SchemaParams, + LocaleParams + > +>( + options?: Options +): Composer< + NonNullable, + NonNullable, + NonNullable, + NonNullable +> +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export function useI18n< + Options extends UseI18nOptions = UseI18nOptions, + Messages = NonNullable, + DateTimeFormats = NonNullable, + NumberFormats = NonNullable, + OptionLocale = NonNullable +>(options: Options = {} as Options) { const instance = getCurrentInstance() - const vm = - instance?.proxy || - (instance as unknown as InstanceType) || - new Vue({}) + // TODO: + if (instance == null) throw new Error('error') + + const vm = instance.proxy + const i18n = vm.$root.$i18n + // TODO: + if (!i18n) throw new Error('vue-i18n not initialized') + // TODO: + if (i18n.mode === 'legacy') throw new Error('vue-i18n is in legacy mode') + + const global = vm.$root.$i18n.global + + // prettier-ignore + const scope: I18nScope = isEmptyObject(options) + ? ('__i18n' in vm.$options) + ? 'local' + : 'global' + : !options.useScope + ? 'local' + : options.useScope + + if (scope === 'global') { + let messages = isObject(options.messages) ? options.messages : {} + if ('__i18nGlobal' in vm.$options) { + messages = getLocaleMessages(global.locale.value as Locale, { + messages, + __i18n: (vm.$options as any).__i18nGlobal + }) + } + // merge locale messages + const locales = Object.keys(messages) + if (locales.length) { + locales.forEach(locale => { + global.mergeLocaleMessage(locale, messages[locale]) + }) + } + + // merge datetime formats + if (isObject(options.datetimeFormats)) { + const locales = Object.keys(options.datetimeFormats) + if (locales.length) { + locales.forEach(locale => { + global.mergeDateTimeFormat(locale, options.datetimeFormats![locale]) + }) + } + } + // merge number formats + if (isObject(options.numberFormats)) { + const locales = Object.keys(options.numberFormats) + if (locales.length) { + locales.forEach(locale => { + global.mergeNumberFormat(locale, options.numberFormats![locale]) + }) + } + } + + return global as Composer< + Messages, + DateTimeFormats, + NumberFormats, + OptionLocale + > + } const locale = computed({ get() { - return i18n.locale + return global.locale }, set(v: string) { - i18n.locale = v + global.locale = v } }) return { - locale, - t: vm.$t.bind(vm), - tc: vm.$tc.bind(vm), - d: vm.$d.bind(vm), - te: vm.$te.bind(vm), - n: vm.$n.bind(vm) - } + locale + // t: vm.$t.bind(vm), + // tc: vm.$tc.bind(vm), + // d: vm.$d.bind(vm), + // te: vm.$te.bind(vm), + // n: vm.$n.bind(vm) + } as unknown as Composer< + Messages, + DateTimeFormats, + NumberFormats, + OptionLocale + > } diff --git a/test/index.test.ts b/test/index.test.ts index 54a0502..e274869 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -3,30 +3,166 @@ */ import CompositionAPI, { nextTick } from '@vue/composition-api' import { createLocalVue, mount } from '@vue/test-utils' +import Vue from 'vue' import VueI18n from 'vue-i18n' import { createI18n, useI18n } from '../src/index' -const container = document.createElement('div') -document.body.appendChild(container) +let localVue beforeEach(() => { - container.innerHTML = '' + localVue = createLocalVue() + // @ts-ignore + localVue.use(VueI18n, { composition: true }) }) -test('createI18n', () => { - const localVue = createLocalVue() - localVue.use(VueI18n) - const i18n = createI18n({ - locale: 'en', - fallbackLocale: 'en', - messages: { - en: { hello: 'Hello' }, - ja: { hello: 'こんにちは' } - } +afterEach(() => { + localVue = null +}) + +describe('createI18n', () => { + describe('legacy mode', () => { + test('basic', () => { + const i18n = createI18n({ + legacy: true, + locale: 'en-US', + fallbackLocale: 'en-US', + messages: { + 'en-US': { hello: 'Hello' }, + 'ja-JP': { hello: 'こんにちは' } + }, + datetimeFormats: { + 'en-US': { + short: { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + timeZone: 'America/New_York' + } + }, + 'ja-JP': { + long: { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + timeZone: 'Asia/Tokyo' + }, + short: { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + timeZone: 'Asia/Tokyo' + } + } + }, + numberFormats: { + 'en-US': { + currency: { + style: 'currency', + currency: 'USD', + currencyDisplay: 'symbol' + }, + decimal: { + style: 'decimal', + useGrouping: false + } + }, + 'ja-JP': { + currency: { + style: 'currency', + currency: 'JPY' + }, + numeric: { + style: 'decimal', + useGrouping: false + }, + percent: { + style: 'percent', + useGrouping: false + } + } + } + }) + expect(i18n.global).toEqual(i18n) // back-port from vue-i18n-next + expect(i18n.mode).toEqual('legacy') // back-port from vue-i18n-next + expect(i18n.locale).toEqual('en-US') + expect(i18n.fallbackLocale).toEqual('en-US') + expect(i18n.t('hello')).toEqual('Hello') + expect( + i18n.d(new Date(Date.UTC(2012, 11, 20, 3, 0, 0)), 'short', 'ja-JP') + ).toEqual('2012/12/20 12:00') + expect(i18n.n(0.99, 'percent', 'ja-JP')).toEqual('99%') + }) + + test('legacy default value', () => { + const i18n = createI18n({ legacy: true }) + expect(i18n.locale).toEqual('en-US') + expect(i18n.fallbackLocale).toEqual('en-US') + expect(i18n.messages).toEqual({}) + expect(i18n.dateTimeFormats).toEqual({}) + expect(i18n.numberFormats).toEqual({}) + expect(i18n.missing).toEqual(null) + expect(i18n.pluralizationRules).toEqual({}) + expect(i18n.postTranslation).toEqual(null) + expect(i18n.preserveDirectiveContent).toEqual(false) + expect(i18n.silentFallbackWarn).toEqual(false) + expect(i18n.silentTranslationWarn).toEqual(false) + expect(i18n.formatFallbackMessages).toEqual(false) + expect(i18n.warnHtmlInMessage).toEqual('off') + expect((i18n as any)._escapeParameterHtml).toEqual(false) + expect((i18n as any)._modifiers).toEqual({}) + expect((i18n as any)._componentInstanceCreatedListener).toEqual(null) + }) + }) + + describe('composition mode', () => { + test.todo('basic') + + test('composition default value', () => { + const i18n = createI18n({ legacy: false }) + expect(i18n.locale).toEqual('en-US') + expect(i18n.fallbackLocale).toEqual('en-US') + expect(i18n.messages).toEqual({}) + expect(i18n.dateTimeFormats).toEqual({}) + expect(i18n.numberFormats).toEqual({}) + expect(i18n.missing).toEqual(null) + expect(i18n.pluralizationRules).toEqual({}) + expect(i18n.postTranslation).toEqual(null) + expect(i18n.preserveDirectiveContent).toEqual(false) + expect(i18n.silentFallbackWarn).toEqual(false) + expect(i18n.silentTranslationWarn).toEqual(false) + expect(i18n.formatFallbackMessages).toEqual(false) + expect(i18n.warnHtmlInMessage).toEqual('off') + expect((i18n as any)._escapeParameterHtml).toEqual(false) + expect((i18n as any)._modifiers).toEqual({}) + expect((i18n as any)._componentInstanceCreatedListener).toEqual(null) + }) + + test('composition specify value', () => { + const pluralRules = { en: () => 0 } + const i18n = createI18n({ + legacy: false, + inheritLocale: false, + fallbackWarn: true, + missingWarn: true, + fallbackFormat: true, + warnHtmlMessage: true, + pluralRules + }) + expect(i18n.pluralizationRules).toEqual(pluralRules) + expect(i18n.silentFallbackWarn).toEqual(false) + expect(i18n.silentTranslationWarn).toEqual(false) + expect(i18n.formatFallbackMessages).toEqual(false) + expect(i18n.warnHtmlInMessage).toEqual('warn') + expect((i18n as any)._sync).toEqual(false) + }) }) - expect(i18n.locale).toBe('en') - expect(i18n.fallbackLocale).toBe('en') - expect(i18n.t('hello')).toBe('Hello') }) /* @@ -60,3 +196,28 @@ test('useI18n', async () => { expect(wrapper.text()).toEqual('Hello') }) */ + +/* +test('mount', async () => { + const localVue = createLocalVue() + localVue.use(VueI18n) + + const i18n = new VueI18n({ + locale: 'en', + messages: { + en: { hello: 'hello' }, + ja: { hello: 'こんにちは' } + } + }) + + const wrapper = mount( + { + template: `

{{ $t('hello') }}

` + }, + { localVue, i18n } + ) + + await Vue.nextTick() + expect(wrapper.text()).toEqual('hello') +}) +*/