diff --git a/.gitignore b/.gitignore index efc1f78a1..e4743cfea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ dist docs/api/*.md !docs/api/injection.md -types +packages/vue-i18n/index.html temp coverage node_modules diff --git a/.vscode/settings.json b/.vscode/settings.json index 36fd2c3e4..9e0933300 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,5 +10,6 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, - "typescript.tsdk": "node_modules/typescript/lib" + "typescript.tsdk": "node_modules/typescript/lib", + "volar.tsPlugin": true } diff --git a/examples/lazy-loading/vite/package.json b/examples/lazy-loading/vite/package.json index 3248044b0..2d3e154d8 100644 --- a/examples/lazy-loading/vite/package.json +++ b/examples/lazy-loading/vite/package.json @@ -6,15 +6,15 @@ "dev": "vite" }, "dependencies": { - "vue": "^3.0.7", - "vue-i18n": "^9.0.0", - "vue-router": "^4.0.5" + "vue": "^3.0.11", + "vue-i18n": "link:../../packages/vue-i18n", + "vue-router": "^4.0.8" }, "devDependencies": { - "@intlify/vite-plugin-vue-i18n": "^2.0.2", - "@vitejs/plugin-vue": "^1.1.5", - "@vue/compiler-sfc": "^3.0.7", - "vite": "^2.0.5" + "@intlify/vite-plugin-vue-i18n": "^2.1.2", + "@vitejs/plugin-vue": "^1.2.2", + "@vue/compiler-sfc": "^3.0.11", + "vite": "^2.3.0" }, "private": true } diff --git a/examples/lazy-loading/vite/src/App.vue b/examples/lazy-loading/vite/src/App.vue index 203cc969b..8777c7a5b 100644 --- a/examples/lazy-loading/vite/src/App.vue +++ b/examples/lazy-loading/vite/src/App.vue @@ -27,7 +27,7 @@ - + + diff --git a/examples/type-safe/package.json b/examples/type-safe/package.json new file mode 100644 index 000000000..1c3ffdc99 --- /dev/null +++ b/examples/type-safe/package.json @@ -0,0 +1,21 @@ +{ + "name": "type-safe", + "version": "0.0.0", + "private": true, + "scripts": { + "dev": "vite", + "build": "vue-tsc --noEmit && vite build", + "serve": "vite preview" + }, + "dependencies": { + "vue": "^3.0.11", + "vue-i18n": "link:../packages/vue-i18n" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^1.2.2", + "@vue/compiler-sfc": "^3.0.11", + "typescript": "^4.1.3", + "vite": "^2.3.3", + "vue-tsc": "^0.0.24" + } +} diff --git a/examples/type-safe/public/favicon.ico b/examples/type-safe/public/favicon.ico new file mode 100644 index 000000000..df36fcfb7 Binary files /dev/null and b/examples/type-safe/public/favicon.ico differ diff --git a/examples/type-safe/src/App.vue b/examples/type-safe/src/App.vue new file mode 100644 index 000000000..2434165c8 --- /dev/null +++ b/examples/type-safe/src/App.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/examples/type-safe/src/assets/logo.png b/examples/type-safe/src/assets/logo.png new file mode 100644 index 000000000..f3d2503fc Binary files /dev/null and b/examples/type-safe/src/assets/logo.png differ diff --git a/examples/type-safe/src/components/GlobalScope.vue b/examples/type-safe/src/components/GlobalScope.vue new file mode 100644 index 000000000..46f0cb7a9 --- /dev/null +++ b/examples/type-safe/src/components/GlobalScope.vue @@ -0,0 +1,23 @@ + + + diff --git a/examples/type-safe/src/components/LocalScope.vue b/examples/type-safe/src/components/LocalScope.vue new file mode 100644 index 000000000..ad8d223a7 --- /dev/null +++ b/examples/type-safe/src/components/LocalScope.vue @@ -0,0 +1,48 @@ + + + diff --git a/examples/type-safe/src/components/en-US.json b/examples/type-safe/src/components/en-US.json new file mode 100644 index 000000000..5d72ea763 --- /dev/null +++ b/examples/type-safe/src/components/en-US.json @@ -0,0 +1,5 @@ +{ + "messages": { + "hello": "Hello, {name}!" + } +} diff --git a/examples/type-safe/src/locales/en-US.json b/examples/type-safe/src/locales/en-US.json new file mode 100644 index 000000000..6ce38d94d --- /dev/null +++ b/examples/type-safe/src/locales/en-US.json @@ -0,0 +1,3 @@ +{ + "hello": "hello world!" +} diff --git a/examples/type-safe/src/locales/schema.ts b/examples/type-safe/src/locales/schema.ts new file mode 100644 index 000000000..d6200f382 --- /dev/null +++ b/examples/type-safe/src/locales/schema.ts @@ -0,0 +1,17 @@ +/** + * define the resource schema + */ + +import enUS from './en-US.json' + +// define message schema as master mesage schema +export type MessageSchema = typeof enUS + +// define number format schema +export type NumberSchema = { + currency: { + style: 'currency' + currencyDisplay: 'symbol' + currency: string + } +} diff --git a/examples/type-safe/src/main.ts b/examples/type-safe/src/main.ts new file mode 100644 index 000000000..66708efaf --- /dev/null +++ b/examples/type-safe/src/main.ts @@ -0,0 +1,40 @@ +import { createApp } from 'vue' +import App from './App.vue' + +import { createI18n } from 'vue-i18n' +import enUS from './locales/en-US.json' + +import type { MessageSchema, NumberSchema } from './locales/schema' +// type MessageSchema = typeof enUS + +/** + * if you can specify resource schema to type parameter of `createI18n`, + * you can make to be type-safe the i18n resources. + */ + +// const i18n = createI18n<[MessageSchema], 'en-US'>({ +const i18n = createI18n< + { + message: MessageSchema + number: NumberSchema + }, + 'en-US', + false +>({ + locale: 'en-US', + legacy: false, + messages: { + 'en-US': enUS + }, + numberFormats: { + 'en-US': { + currency: { + style: 'currency', + currencyDisplay: 'symbol', + currency: 'USD' + } + } + } +}) + +createApp(App).use(i18n).mount('#app') diff --git a/examples/type-safe/src/shims-vue.d.ts b/examples/type-safe/src/shims-vue.d.ts new file mode 100644 index 000000000..ac1ded792 --- /dev/null +++ b/examples/type-safe/src/shims-vue.d.ts @@ -0,0 +1,5 @@ +declare module '*.vue' { + import { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/examples/type-safe/tsconfig.json b/examples/type-safe/tsconfig.json new file mode 100644 index 000000000..e754e6529 --- /dev/null +++ b/examples/type-safe/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "moduleResolution": "node", + "strict": true, + "jsx": "preserve", + "sourceMap": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "lib": ["esnext", "dom"], + "types": ["vite/client"] + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] +} diff --git a/examples/type-safe/vite.config.ts b/examples/type-safe/vite.config.ts new file mode 100644 index 000000000..315212d69 --- /dev/null +++ b/examples/type-safe/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue()] +}) diff --git a/examples/type-safe/yarn.lock b/examples/type-safe/yarn.lock new file mode 100644 index 000000000..7faf7a498 --- /dev/null +++ b/examples/type-safe/yarn.lock @@ -0,0 +1,619 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/helper-validator-identifier@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" + integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== + +"@babel/parser@^7.12.0", "@babel/parser@^7.13.9": + version "7.14.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.3.tgz#9b530eecb071fd0c93519df25c5ff9f14759f298" + integrity sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ== + +"@babel/types@^7.12.0", "@babel/types@^7.13.0": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.2.tgz#4208ae003107ef8a057ea8333e56eb64d2f6a2c3" + integrity sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw== + dependencies: + "@babel/helper-validator-identifier" "^7.14.0" + to-fast-properties "^2.0.0" + +"@vitejs/plugin-vue@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-1.2.2.tgz#b0038fc11b9099f4cd01fcbf0ee419adda417b52" + integrity sha512-5BI2WFfs/Z0pAV4S/IQf1oH3bmFYlL5ATMBHgTt1Lf7hAnfpNd5oUAAs6hZPfk3QhvyUQgtk0rJBlabwNFcBJQ== + +"@vue/compiler-core@3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.0.11.tgz#5ef579e46d7b336b8735228758d1c2c505aae69a" + integrity sha512-6sFj6TBac1y2cWCvYCA8YzHJEbsVkX7zdRs/3yK/n1ilvRqcn983XvpBbnN3v4mZ1UiQycTvOiajJmOgN9EVgw== + dependencies: + "@babel/parser" "^7.12.0" + "@babel/types" "^7.12.0" + "@vue/shared" "3.0.11" + estree-walker "^2.0.1" + source-map "^0.6.1" + +"@vue/compiler-dom@3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.0.11.tgz#b15fc1c909371fd671746020ba55b5dab4a730ee" + integrity sha512-+3xB50uGeY5Fv9eMKVJs2WSRULfgwaTJsy23OIltKgMrynnIj8hTYY2UL97HCoz78aDw1VDXdrBQ4qepWjnQcw== + dependencies: + "@vue/compiler-core" "3.0.11" + "@vue/shared" "3.0.11" + +"@vue/compiler-sfc@^3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.0.11.tgz#cd8ca2154b88967b521f5ad3b10f5f8b6b665679" + integrity sha512-7fNiZuCecRleiyVGUWNa6pn8fB2fnuJU+3AGjbjl7r1P5wBivfl02H4pG+2aJP5gh2u+0wXov1W38tfWOphsXw== + dependencies: + "@babel/parser" "^7.13.9" + "@babel/types" "^7.13.0" + "@vue/compiler-core" "3.0.11" + "@vue/compiler-dom" "3.0.11" + "@vue/compiler-ssr" "3.0.11" + "@vue/shared" "3.0.11" + consolidate "^0.16.0" + estree-walker "^2.0.1" + hash-sum "^2.0.0" + lru-cache "^5.1.1" + magic-string "^0.25.7" + merge-source-map "^1.1.0" + postcss "^8.1.10" + postcss-modules "^4.0.0" + postcss-selector-parser "^6.0.4" + source-map "^0.6.1" + +"@vue/compiler-ssr@3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.0.11.tgz#ac5a05fd1257412fa66079c823d8203b6a889a13" + integrity sha512-66yUGI8SGOpNvOcrQybRIhl2M03PJ+OrDPm78i7tvVln86MHTKhM3ERbALK26F7tXl0RkjX4sZpucCpiKs3MnA== + dependencies: + "@vue/compiler-dom" "3.0.11" + "@vue/shared" "3.0.11" + +"@vue/reactivity@3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.0.11.tgz#07b588349fd05626b17f3500cbef7d4bdb4dbd0b" + integrity sha512-SKM3YKxtXHBPMf7yufXeBhCZ4XZDKP9/iXeQSC8bBO3ivBuzAi4aZi0bNoeE2IF2iGfP/AHEt1OU4ARj4ao/Xw== + dependencies: + "@vue/shared" "3.0.11" + +"@vue/runtime-core@3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.0.11.tgz#c52dfc6acf3215493623552c1c2919080c562e44" + integrity sha512-87XPNwHfz9JkmOlayBeCCfMh9PT2NBnv795DSbi//C/RaAnc/bGZgECjmkD7oXJ526BZbgk9QZBPdFT8KMxkAg== + dependencies: + "@vue/reactivity" "3.0.11" + "@vue/shared" "3.0.11" + +"@vue/runtime-dom@3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.0.11.tgz#7a552df21907942721feb6961c418e222a699337" + integrity sha512-jm3FVQESY3y2hKZ2wlkcmFDDyqaPyU3p1IdAX92zTNeCH7I8zZ37PtlE1b9NlCtzV53WjB4TZAYh9yDCMIEumA== + dependencies: + "@vue/runtime-core" "3.0.11" + "@vue/shared" "3.0.11" + csstype "^2.6.8" + +"@vue/shared@3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.0.11.tgz#20d22dd0da7d358bb21c17f9bde8628152642c77" + integrity sha512-b+zB8A2so8eCE0JsxjL24J7vdGl8rzPQ09hZNhystm+KqSbKcAej1A+Hbva1rCMmTTqA+hFnUSDc5kouEo0JzA== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +big-integer@^1.6.17: + version "1.6.48" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e" + integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w== + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" + integrity sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk= + dependencies: + buffers "~0.1.1" + chainsaw "~0.1.0" + +bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bluebird@~3.4.1: + version "3.4.7" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" + integrity sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +buffer-indexof-polyfill@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c" + integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A== + +buffers@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" + integrity sha1-skV5w77U1tOWru5tmorn9Ugqt7s= + +chainsaw@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" + integrity sha1-XqtQsor+WAdNDVgpE4iCi15fvJg= + dependencies: + traverse ">=0.3.0 <0.4" + +colorette@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" + integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +consolidate@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.16.0.tgz#a11864768930f2f19431660a65906668f5fbdc16" + integrity sha512-Nhl1wzCslqXYTJVDyJCu3ODohy9OfBMB5uD2BiBTzd7w+QY0lBzafkR8y8755yMYHAaMD4NuzbAw03/xzfw+eQ== + dependencies: + bluebird "^3.7.2" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +csstype@^2.6.8: + version "2.6.17" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.17.tgz#4cf30eb87e1d1a005d8b6510f95292413f6a1c0e" + integrity sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A== + +duplexer2@~0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME= + dependencies: + readable-stream "^2.0.2" + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +esbuild@^0.11.23: + version "0.11.23" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.11.23.tgz#c42534f632e165120671d64db67883634333b4b8" + integrity sha512-iaiZZ9vUF5wJV8ob1tl+5aJTrwDczlvGP0JoMmnpC2B0ppiMCu8n8gmy5ZTGl5bcG081XBVn+U+jP+mPFm5T5Q== + +estree-walker@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +fstream@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" + integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +generic-names@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/generic-names/-/generic-names-2.0.1.tgz#f8a378ead2ccaa7a34f0317b05554832ae41b872" + integrity sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ== + dependencies: + loader-utils "^1.1.0" + +glob@^7.1.3: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.2.2: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-sum@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a" + integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg== + +icss-replace-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= + +icss-utils@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@~2.0.0, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-core-module@^2.2.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" + integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== + dependencies: + has "^1.0.3" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +listenercount@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937" + integrity sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc= + +loader-utils@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +magic-string@^0.25.7: + version "0.25.7" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" + integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== + dependencies: + sourcemap-codec "^1.4.4" + +merge-source-map@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" + integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== + dependencies: + source-map "^0.6.1" + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +"mkdirp@>=0.5 0": + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +nanoid@^3.1.23: + version "3.1.23" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" + integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== + +postcss-modules-local-by-default@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" + integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" + integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-modules@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules/-/postcss-modules-4.0.0.tgz#2bc7f276ab88f3f1b0fadf6cbd7772d43b5f3b9b" + integrity sha512-ghS/ovDzDqARm4Zj6L2ntadjyQMoyJmi0JkLlYtH2QFLrvNlxH5OAVRPWPeKilB0pY7SbuhO173KOWkPAxRJcw== + dependencies: + generic-names "^2.0.1" + icss-replace-symbols "^1.1.0" + lodash.camelcase "^4.3.0" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + string-hash "^1.1.1" + +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: + version "6.0.6" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea" + integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" + integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== + +postcss@^8.1.10, postcss@^8.2.10: + version "8.3.0" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.0.tgz#b1a713f6172ca427e3f05ef1303de8b65683325f" + integrity sha512-+ogXpdAjWGa+fdYY5BQ96V/6tAo+TdSSIMP5huJBIygdWwKtVoB5JWZ7yUd4xZ8r+8Kvvx4nyg/PQ071H4UtcQ== + dependencies: + colorette "^1.2.2" + nanoid "^3.1.23" + source-map-js "^0.6.2" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +readable-stream@^2.0.2, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +resolve@^1.19.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + +rimraf@2: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rollup@^2.38.5: + version "2.49.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.49.0.tgz#f0e5bb2b770ddf1be8cc30d4cce3457574c8c871" + integrity sha512-UnrCjMXICx9q0jF8L7OYs7LPk95dW0U5UYp/VANnWqfuhyr66FWi/YVlI34Oy8Tp4ZGLcaUDt4APJm80b9oPWQ== + optionalDependencies: + fsevents "~2.3.1" + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +setimmediate@~1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +source-map-js@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" + integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== + +source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sourcemap-codec@^1.4.4: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + +string-hash@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b" + integrity sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs= + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +"traverse@>=0.3.0 <0.4": + version "0.3.9" + resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" + integrity sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk= + +typescript@^4.1.3: + version "4.2.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" + integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== + +unzipper@0.10.11: + version "0.10.11" + resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.10.11.tgz#0b4991446472cbdb92ee7403909f26c2419c782e" + integrity sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw== + dependencies: + big-integer "^1.6.17" + binary "~0.3.0" + bluebird "~3.4.1" + buffer-indexof-polyfill "~1.0.0" + duplexer2 "~0.1.4" + fstream "^1.0.12" + graceful-fs "^4.2.2" + listenercount "~1.0.1" + readable-stream "~2.3.6" + setimmediate "~1.0.4" + +util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +vite@^2.3.3: + version "2.3.4" + resolved "https://registry.yarnpkg.com/vite/-/vite-2.3.4.tgz#370118e0334725b898ff754ea43d5db4c5e120e3" + integrity sha512-7orxrF65+Q5n/sMCnO91S8OS0gkPJ7g+y3bLlc7CPCXVswK8to1T8YycCk9SZh+AcIc0TuN6YajWTBFS5atMNA== + dependencies: + esbuild "^0.11.23" + postcss "^8.2.10" + resolve "^1.19.0" + rollup "^2.38.5" + optionalDependencies: + fsevents "~2.3.1" + +"vue-i18n@link:../packages/vue-i18n": + version "0.0.0" + uid "" + +vue-tsc@^0.0.24: + version "0.0.24" + resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-0.0.24.tgz#0cd90db679f53ea1694254b8663fdb3d624a0872" + integrity sha512-Qx0V7jkWMtvddtaWa1SA8YKkBCRmjq9zZUB2UIMZiso6JSH538oHD2VumSzkoDnAfFbY3t0/j1mB2abpA0bGWA== + dependencies: + unzipper "0.10.11" + +vue@^3.0.11: + version "3.0.11" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.0.11.tgz#c82f9594cbf4dcc869241d4c8dd3e08d9a8f4b5f" + integrity sha512-3/eUi4InQz8MPzruHYSTQPxtM3LdZ1/S/BvaU021zBnZi0laRUyH6pfuE4wtUeLvI8wmUNwj5wrZFvbHUXL9dw== + dependencies: + "@vue/compiler-dom" "3.0.11" + "@vue/runtime-dom" "3.0.11" + "@vue/shared" "3.0.11" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== diff --git a/package.json b/package.json index c3c6f2ea4..f1e85a22d 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "build:size-core": "rollup -c packages/size-check-core/rollup.config.js", "build:size-vue-i18n": "rollup -c packages/size-check-vue-i18n/rollup.config.js", "build:sourcemap": "yarn build --sourcemap", - "build:type": "yarn build --types && tail -n +20 ./packages/vue-i18n/src/vue.d.ts >> ./packages/vue-i18n/dist/vue-i18n.d.ts", + "build:type": "yarn build --types && tail -n +22 ./packages/vue-i18n/src/vue.d.ts >> ./packages/vue-i18n/dist/vue-i18n.d.ts", "clean": "npm-run-all --parallel clean:*", "clean:cache": "yarn clean:cache:jest", "clean:cache:jest": "jest --clearCache", @@ -28,7 +28,7 @@ "clean:dist": "rimraf ./dist ./packages/**/dist ./docs/.vitepress/dist", "clean:docs": "trash './docs/api/!(injection).md'", "clean:duplicate": "yarn-deduplicate yarn.lock", - "clean:type": "rimraf ./packages/**/types ./temp", + "clean:type": "rimraf ./temp", "coverage": "opener coverage/lcov-report/index.html", "dev": "node -r esbuild-register scripts/dev.ts", "dev:e2e": "jest --runInBand --config ./jest.e2e.config.js", diff --git a/packages/core-base/src/context.ts b/packages/core-base/src/context.ts index a455a25cd..6d3c6e4d6 100644 --- a/packages/core-base/src/context.ts +++ b/packages/core-base/src/context.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + import { warn, isString, @@ -28,41 +30,67 @@ import type { } from '@intlify/runtime' import type { VueDevToolsEmitter } from '@intlify/vue-devtools' import type { - MetaInfo, + UnionToTuple, + LocaleRecord, NumberFormat, DateTimeFormat, DateTimeFormats as DateTimeFormatsType, - NumberFormats as NumberFormatsType + NumberFormats as NumberFormatsType, + SchemaParams, + LocaleParams, + PickupLocales, + FallbackLocales } from './types' +export interface MetaInfo { + [field: string]: unknown +} + /** @VueI18nGeneral */ export type LocaleMessageValue = + | LocaleMessageDictionary | string - | MessageFunction - | LocaleMessageDictionary - | LocaleMessageArray +// prettier-ignore /** @VueI18nGeneral */ -export type LocaleMessageDictionary = { - [property: string]: LocaleMessageValue -} +export type LocaleMessageType = T extends string + ? string + : T extends () => Promise + ? LocaleMessageDictionary + : T extends (...args: infer Arguments) => any + ? (...args: Arguments) => ReturnType + : T extends Record + ? LocaleMessageDictionary + : T extends Array + ? { [K in keyof T]: T[K] } + : T + /** @VueI18nGeneral */ -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface LocaleMessageArray - extends Array> {} +export type LocaleMessageDictionary = { + [K in keyof T]: LocaleMessageType +} + /** @VueI18nGeneral */ -export type LocaleMessages = Record< - Locale, - LocaleMessageDictionary +export type LocaleMessage = Record< + string, + LocaleMessageValue > +/** @VueI18nGeneral */ +export type LocaleMessages< + Schema, + Locales = Locale, + Message = string // eslint-disable-line @typescript-eslint/no-unused-vars +> = LocaleRecord, Schema> + export type CoreMissingHandler = ( - context: CoreCommonContext, + context: CoreContext, locale: Locale, key: Path, type: CoreMissingType, ...values: unknown[] ) => string | void + /** @VueI18nGeneral */ export type PostTranslationHandler = ( translated: MessageType @@ -73,13 +101,58 @@ export type MessageCompiler = ( options?: CompileOptions ) => MessageFunction -export interface CoreOptions { +// prettier-ignore +export interface CoreOptions< + Message = string, + Schema extends + { + message?: unknown + datetime?: unknown + number?: unknown + } = { + message: LocaleMessage, + datetime: DateTimeFormat, + number: NumberFormat + }, + Locales extends + | { + messages: unknown + datetimeFormats: unknown + numberFormats: unknown + } + | string = Locale, + MessagesLocales = Locales extends { messages: infer M } + ? M + : Locales extends string + ? Locales + : Locale, + DateTimeFormatsLocales = Locales extends { datetimeFormats: infer D } + ? D + : Locales extends string + ? Locales + : Locale, + NumberFormatsLocales = Locales extends { numberFormats: infer N } + ? N + : Locales extends string + ? Locales + : Locale, + MessageSchema = Schema extends { message: infer M } ? M : LocaleMessage, + DateTimeSchema = Schema extends { datetime: infer D } ? D : DateTimeFormat, + NumberSchema = Schema extends { number: infer N } ? N : NumberFormat, + _Messages extends LocaleMessages< + MessageSchema, + MessagesLocales, + Message + > = LocaleMessages, + _DateTimeFormats extends DateTimeFormatsType = DateTimeFormatsType, + _NumberFormats extends NumberFormatsType = NumberFormatsType, +> { version?: string locale?: Locale fallbackLocale?: FallbackLocale - messages?: LocaleMessages - datetimeFormats?: DateTimeFormatsType - numberFormats?: NumberFormatsType + messages?: { [K in keyof _Messages]: MessageSchema } + datetimeFormats?: { [K in keyof _DateTimeFormats]: DateTimeSchema } + numberFormats?: { [K in keyof _NumberFormats]: NumberSchema } modifiers?: LinkedModifiers pluralRules?: PluralizationRules missing?: CoreMissingHandler @@ -103,11 +176,11 @@ export interface CoreInternalOptions { __meta?: MetaInfo } -export interface CoreCommonContext { +export interface CoreCommonContext { cid: number version: string - locale: Locale - fallbackLocale: FallbackLocale + locale: Locales + fallbackLocale: FallbackLocales missing: CoreMissingHandler | null missingWarn: boolean | RegExp fallbackWarn: boolean | RegExp @@ -116,9 +189,10 @@ export interface CoreCommonContext { onWarn(msg: string, err?: Error): void } -export interface CoreTranslationContext - extends CoreCommonContext { - messages: Messages +export interface CoreTranslationContext { + messages: { + [K in keyof Messages]: Messages[K] + } modifiers: LinkedModifiers pluralRules?: PluralizationRules postTranslation: PostTranslationHandler | null @@ -129,24 +203,28 @@ export interface CoreTranslationContext messageResolver: MessageResolver } -export interface CoreDateTimeContext - extends CoreCommonContext { - datetimeFormats: DateTimeFormats +export interface CoreDateTimeContext { + datetimeFormats: { [K in keyof DateTimeFormats]: DateTimeFormats[K] } } -export interface CoreNumberContext - extends CoreCommonContext { - numberFormats: NumberFormats +export interface CoreNumberContext { + numberFormats: { [K in keyof NumberFormats]: NumberFormats[K] } } -export interface CoreContext< +export type CoreContext< + Message = string, Messages = {}, DateTimeFormats = {}, NumberFormats = {}, - Message = string -> extends CoreTranslationContext, - CoreDateTimeContext, - CoreNumberContext {} + ResourceLocales = + | PickupLocales> + | PickupLocales> + | PickupLocales>, + Locales = [ResourceLocales] extends [never] ? Locale : ResourceLocales +> = CoreCommonContext & + CoreTranslationContext, Message> & + CoreDateTimeContext> & + CoreNumberContext> export interface CoreInternalContext { __datetimeFormatters: Map @@ -207,27 +285,43 @@ let _cid = 0 export function createCoreContext< Message = string, - Options extends CoreOptions = object, - Messages extends Record< - keyof Options['messages'], - LocaleMessageDictionary - > = Record>, - DateTimeFormats extends Record< - keyof Options['datetimeFormats'], - DateTimeFormat - > = Record, - NumberFormats extends Record< - keyof Options['numberFormats'], - NumberFormat - > = Record + Options extends CoreOptions = CoreOptions, + Messages = Options['messages'] extends object ? Options['messages'] : {}, + DateTimeFormats = Options['datetimeFormats'] extends object + ? Options['datetimeFormats'] + : {}, + NumberFormats = Options['numberFormats'] extends object + ? Options['numberFormats'] + : {} >( - options: Options = {} as Options -): CoreContext< - Options['messages'], - Options['datetimeFormats'], - Options['numberFormats'], - Message -> { + options: Options +): CoreContext + +export function createCoreContext< + Schema = LocaleMessage, + Locales = 'en-US', + Message = string, + Options extends CoreOptions< + Message, + SchemaParams, + LocaleParams + > = CoreOptions< + Message, + SchemaParams, + LocaleParams + >, + Messages = Options['messages'] extends object ? Options['messages'] : {}, + DateTimeFormats = Options['datetimeFormats'] extends object + ? Options['datetimeFormats'] + : {}, + NumberFormats = Options['numberFormats'] extends object + ? Options['numberFormats'] + : {} +>( + options: Options +): CoreContext + +export function createCoreContext(options: any = {}): any { // setup options const version = isString(options.version) ? options.version : VERSION const locale = isString(options.locale) ? options.locale : 'en-US' @@ -240,16 +334,16 @@ export function createCoreContext< : locale const messages = isPlainObject(options.messages) ? options.messages - : ({ [locale]: {} } as Messages) + : { [locale]: {} } const datetimeFormats = isPlainObject(options.datetimeFormats) ? options.datetimeFormats - : ({ [locale]: {} } as DateTimeFormats) + : { [locale]: {} } const numberFormats = isPlainObject(options.numberFormats) ? options.numberFormats - : ({ [locale]: {} } as NumberFormats) + : { [locale]: {} } const modifiers = assign( - {} as LinkedModifiers, - options.modifiers || ({} as LinkedModifiers), + {}, + options.modifiers || {}, getDefaultLinkedModifiers() ) const pluralRules = options.pluralRules || {} @@ -317,12 +411,7 @@ export function createCoreContext< __datetimeFormatters, __numberFormatters, __meta - } as CoreContext< - Options['messages'], - Options['datetimeFormats'], - Options['numberFormats'], - Message - > + } // for vue-devtools timeline event if (__DEV__) { @@ -358,7 +447,7 @@ export function isTranslateMissingWarn( /** @internal */ export function handleMissing( - context: CoreCommonContext, + context: CoreContext, key: Path, locale: Locale, missingWarn: boolean | RegExp, @@ -380,7 +469,7 @@ export function handleMissing( } if (missing !== null) { - const ret = missing(context, locale, key, type) + const ret = missing(context as any, locale, key, type) return isString(ret) ? ret : key } else { if (__DEV__ && isTranslateMissingWarn(missingWarn, key)) { @@ -392,7 +481,7 @@ export function handleMissing( /** @internal */ export function getLocaleChain( - ctx: CoreCommonContext, + ctx: CoreContext, fallback: FallbackLocale, start: Locale ): Locale[] { @@ -489,11 +578,13 @@ function appendItemToChain( /** @internal */ export function updateFallbackLocale( - ctx: CoreCommonContext, + ctx: CoreContext, locale: Locale, fallback: FallbackLocale ): void { const context = (ctx as unknown) as CoreInternalContext context.__localeChainCache = new Map() - getLocaleChain(ctx, fallback, locale) + getLocaleChain(ctx, fallback, locale) } + +/* eslint-enable @typescript-eslint/no-explicit-any */ diff --git a/packages/core-base/src/datetime.ts b/packages/core-base/src/datetime.ts index 2607db05a..5c7584530 100644 --- a/packages/core-base/src/datetime.ts +++ b/packages/core-base/src/datetime.ts @@ -19,13 +19,14 @@ import { CoreErrorCodes, createCoreError } from './errors' import { VueDevToolsTimelineEvents } from '@intlify/vue-devtools' import { Availabilities } from './intl' -import type { Locale } from '@intlify/runtime' +import type { Locale, FallbackLocale } from '@intlify/runtime' import type { DateTimeFormat, DateTimeFormats as DateTimeFormatsType, - DateTimeFormatOptions -} from './types' -import type { CoreDateTimeContext, CoreInternalContext } from './context' + DateTimeFormatOptions, + PickupFormatKeys +} from './types/index' +import type { CoreContext, CoreInternalContext } from './context' /** * # datetime @@ -75,17 +76,17 @@ import type { CoreDateTimeContext, CoreInternalContext } from './context' * * @VueI18nGeneral */ -export interface DateTimeOptions { +export interface DateTimeOptions { /** * @remarks * The target format key */ - key?: string + key?: Key /** * @remarks * The locale of localization */ - locale?: Locale + locale?: Locales /** * @remarks * Whether suppress warnings outputted when localization fails @@ -103,35 +104,96 @@ export interface DateTimeOptions { part?: boolean } -// `datetime` function overloads -export function datetime( - context: CoreDateTimeContext, - value: number | Date +/** + * `datetime` function overloads + */ + +export function datetime< + Context extends CoreContext, + Message = string +>( + context: Context, + value: number | string | Date ): string | number | Intl.DateTimeFormatPart[] -export function datetime( - context: CoreDateTimeContext, - value: number | Date, - key: string + +export function datetime< + Context extends CoreContext, + Value extends number | string | Date = number, + Key extends string = string, + ResourceKeys extends PickupFormatKeys< + Context['datetimeFormats'] + > = PickupFormatKeys, + Message = string +>( + context: Context, + value: Value, + keyOrOptions: + | Key + | ResourceKeys + | DateTimeOptions ): string | number | Intl.DateTimeFormatPart[] -export function datetime( - context: CoreDateTimeContext, - value: number | Date, - key: string, - locale: Locale + +export function datetime< + Context extends CoreContext, + Value extends number | string | Date = number, + Key extends string = string, + ResourceKeys extends PickupFormatKeys< + Context['datetimeFormats'] + > = PickupFormatKeys, + Message = string +>( + context: Context, + value: Value, + keyOrOptions: + | Key + | ResourceKeys + | DateTimeOptions, + locale: Context['locale'] ): string | number | Intl.DateTimeFormatPart[] -export function datetime( - context: CoreDateTimeContext, - value: number | Date, - options: DateTimeOptions + +export function datetime< + Context extends CoreContext, + Value extends number | string | Date = number, + Key extends string = string, + ResourceKeys extends PickupFormatKeys< + Context['datetimeFormats'] + > = PickupFormatKeys, + Message = string +>( + context: Context, + value: Value, + keyOrOptions: + | Key + | ResourceKeys + | DateTimeOptions, + override: Intl.DateTimeFormatOptions +): string | number | Intl.DateTimeFormatPart[] + +export function datetime< + Context extends CoreContext, + Value extends number | string | Date = number, + Key extends string = string, + ResourceKeys extends PickupFormatKeys< + Context['datetimeFormats'] + > = PickupFormatKeys, + Message = string +>( + context: Context, + value: Value, + keyOrOptions: + | Key + | ResourceKeys + | DateTimeOptions, + locale: Context['locale'], + override: Intl.DateTimeFormatOptions ): string | number | Intl.DateTimeFormatPart[] -export function datetime( - context: CoreDateTimeContext, - ...args: unknown[] -): string | number | Intl.DateTimeFormatPart[] // for internal // implementation of `datetime` function -export function datetime( - context: CoreDateTimeContext, +export function datetime< + Context extends CoreContext, + Message = string +>( + context: Context, ...args: unknown[] ): string | number | Intl.DateTimeFormatPart[] { const { datetimeFormats, unresolving, fallbackLocale, onWarn } = context @@ -151,7 +213,11 @@ export function datetime( : context.fallbackWarn const part = !!options.part const locale = isString(options.locale) ? options.locale : context.locale - const locales = getLocaleChain(context, fallbackLocale, locale) + const locales = getLocaleChain( + context as any, // eslint-disable-line @typescript-eslint/no-explicit-any + fallbackLocale as FallbackLocale, + locale + ) if (!isString(key) || key === '') { return new Intl.DateTimeFormat(locale).format(value) @@ -199,7 +265,7 @@ export function datetime( format = datetimeFormat[key] if (isPlainObject(format)) break - handleMissing(context, key, targetLocale, missingWarn, type) + handleMissing(context as any, key, targetLocale, missingWarn, type) // eslint-disable-line @typescript-eslint/no-explicit-any from = to } @@ -279,7 +345,7 @@ export function parseDateTimeArgs( /** @internal */ export function clearDateTimeFormat( - ctx: CoreDateTimeContext, + ctx: CoreContext, locale: Locale, format: DateTimeFormat ): void { diff --git a/packages/core-base/src/number.ts b/packages/core-base/src/number.ts index aca35072f..4720a2f6b 100644 --- a/packages/core-base/src/number.ts +++ b/packages/core-base/src/number.ts @@ -18,13 +18,14 @@ import { CoreErrorCodes, createCoreError } from './errors' import { VueDevToolsTimelineEvents } from '@intlify/vue-devtools' import { Availabilities } from './intl' -import type { Locale } from '@intlify/runtime' +import type { Locale, FallbackLocale } from '@intlify/runtime' import type { NumberFormat, NumberFormats as NumberFormatsType, - NumberFormatOptions + NumberFormatOptions, + PickupFormatKeys } from './types' -import type { CoreNumberContext, CoreInternalContext } from './context' +import type { CoreContext, CoreInternalContext } from './context' /** * # number @@ -73,17 +74,17 @@ import type { CoreNumberContext, CoreInternalContext } from './context' * * @VueI18nGeneral */ -export interface NumberOptions { +export interface NumberOptions { /** * @remarks * The target format key */ - key?: string + key?: Key /** * @remarks * The locale of localization */ - locale?: Locale + locale?: Locales /** * @remarks * Whether suppress warnings outputted when localization fails @@ -101,35 +102,93 @@ export interface NumberOptions { part?: boolean } -// `number` function overloads -export function number( - context: CoreNumberContext, - value: number +/** + * `number` function overloads + */ + +export function number< + Context extends CoreContext, + Message = string +>(context: Context, value: number): string | number | Intl.NumberFormatPart[] + +export function number< + Context extends CoreContext, + Value extends number = number, + Key extends string = string, + ResourceKeys extends PickupFormatKeys< + Context['numberFormats'] + > = PickupFormatKeys, + Message = string +>( + context: Context, + value: Value, + keyOrOptions: + | Key + | ResourceKeys + | NumberOptions ): string | number | Intl.NumberFormatPart[] -export function number( - context: CoreNumberContext, - value: number, - key: string + +export function number< + Context extends CoreContext, + Value extends number = number, + Key extends string = string, + ResourceKeys extends PickupFormatKeys< + Context['numberFormats'] + > = PickupFormatKeys, + Message = string +>( + context: Context, + value: Value, + keyOrOptions: + | Key + | ResourceKeys + | NumberOptions, + locale: Context['locale'] ): string | number | Intl.NumberFormatPart[] -export function number( - context: CoreNumberContext, - value: number, - key: string, - locale: Locale + +export function number< + Context extends CoreContext, + Value extends number = number, + Key extends string = string, + ResourceKeys extends PickupFormatKeys< + Context['numberFormats'] + > = PickupFormatKeys, + Message = string +>( + context: Context, + value: Value, + keyOrOptions: + | Key + | ResourceKeys + | NumberOptions, + override: Intl.NumberFormatOptions ): string | number | Intl.NumberFormatPart[] -export function number( - context: CoreNumberContext, - value: number, - options: NumberOptions + +export function number< + Context extends CoreContext, + Value extends number = number, + Key extends string = string, + ResourceKeys extends PickupFormatKeys< + Context['numberFormats'] + > = PickupFormatKeys, + Message = string +>( + context: Context, + value: Value, + keyOrOptions: + | Key + | ResourceKeys + | NumberOptions, + locale: Context['locale'], + override: Intl.NumberFormatOptions ): string | number | Intl.NumberFormatPart[] -export function number( - context: CoreNumberContext, - ...args: unknown[] -): string | number | Intl.NumberFormatPart[] // for internal // implementation of `number` function -export function number( - context: CoreNumberContext, +export function number< + Context extends CoreContext, + Message = string +>( + context: Context, ...args: unknown[] ): string | number | Intl.NumberFormatPart[] { const { numberFormats, unresolving, fallbackLocale, onWarn } = context @@ -149,7 +208,11 @@ export function number( : context.fallbackWarn const part = !!options.part const locale = isString(options.locale) ? options.locale : context.locale - const locales = getLocaleChain(context, fallbackLocale, locale) + const locales = getLocaleChain( + context as any, // eslint-disable-line @typescript-eslint/no-explicit-any + fallbackLocale as FallbackLocale, + locale + ) if (!isString(key) || key === '') { return new Intl.NumberFormat(locale).format(value) @@ -197,7 +260,7 @@ export function number( format = numberFormat[key] if (isPlainObject(format)) break - handleMissing(context, key, targetLocale, missingWarn, type) + handleMissing(context as any, key, targetLocale, missingWarn, type) // eslint-disable-line @typescript-eslint/no-explicit-any from = to } @@ -256,7 +319,7 @@ export function parseNumberArgs( /** @internal */ export function clearNumberFormat( - ctx: CoreNumberContext, + ctx: CoreContext, locale: Locale, format: NumberFormat ): void { diff --git a/packages/core-base/src/translate.ts b/packages/core-base/src/translate.ts index 708ae2a5e..31688fa46 100644 --- a/packages/core-base/src/translate.ts +++ b/packages/core-base/src/translate.ts @@ -22,7 +22,8 @@ import { handleMissing, getLocaleChain, NOT_REOSLVED, - getAdditionalMeta + getAdditionalMeta, + CoreContext } from './context' import { CoreWarnCodes, getWarnMessage } from './warnings' import { CoreErrorCodes, createCoreError } from './errors' @@ -45,9 +46,9 @@ import type { AdditionalPayloads } from '@intlify/devtools-if' import type { LocaleMessages, LocaleMessageValue, - CoreInternalContext, - CoreTranslationContext + CoreInternalContext } from './context' +import type { PickupKeys } from './types' const NOOP_MESSAGE_FUNCTION = () => '' export const isMessageFunction = (val: unknown): val is MessageFunction => @@ -105,7 +106,7 @@ export const isMessageFunction = (val: unknown): val is MessageFunction => * * @VueI18nGeneral */ -export interface TranslateOptions { +export interface TranslateOptions { /** * @remarks * List interpolation @@ -130,7 +131,7 @@ export interface TranslateOptions { * @remarks * The locale of localization */ - locale?: Locale + locale?: Locales /** * @remarks * Whether suppress warnings outputted when localization fails @@ -153,107 +154,191 @@ export interface TranslateOptions { resolvedMessage?: boolean } -// `translate` function overloads -export function translate( - context: CoreTranslationContext, - key: Path | number +/** + * `translate` function overloads + */ + +export function translate< + Context extends CoreContext, + Key extends string = string, + ResourceKeys extends PickupKeys = PickupKeys< + Context['messages'] + >, + Message = string +>( + context: Context, + key: Key | ResourceKeys | number | MessageFunction ): MessageType | number -export function translate( - context: CoreTranslationContext, - key: Path | number, + +export function translate< + Context extends CoreContext, + Key extends string = string, + ResourceKeys extends PickupKeys = PickupKeys< + Context['messages'] + >, + Message = string +>( + context: Context, + key: Key | ResourceKeys | number | MessageFunction, plural: number ): MessageType | number -export function translate( - context: CoreTranslationContext, - key: Path | number, - plural: number, - options: TranslateOptions -): MessageType | number -export function translate( - context: CoreTranslationContext, - message: MessageFunction | string, + +export function translate< + Context extends CoreContext, + Key extends string = string, + ResourceKeys extends PickupKeys = PickupKeys< + Context['messages'] + >, + Message = string +>( + context: Context, + key: Key | ResourceKeys | number | MessageFunction, plural: number, - options: TranslateOptions + options: TranslateOptions ): MessageType | number -export function translate( - context: CoreTranslationContext, - key: Path | number, + +export function translate< + Context extends CoreContext, + Key extends string = string, + ResourceKeys extends PickupKeys = PickupKeys< + Context['messages'] + >, + Message = string +>( + context: Context, + key: Key | ResourceKeys | number | MessageFunction, defaultMsg: string ): MessageType | number -export function translate( - context: CoreTranslationContext, - key: Path | number, + +export function translate< + Context extends CoreContext, + Key extends string = string, + ResourceKeys extends PickupKeys = PickupKeys< + Context['messages'] + >, + Message = string +>( + context: Context, + key: Key | ResourceKeys | number | MessageFunction, defaultMsg: string, - options: TranslateOptions + options: TranslateOptions ): MessageType | number -export function translate( - context: CoreTranslationContext, - key: Path | number, + +export function translate< + Context extends CoreContext, + Key extends string = string, + ResourceKeys extends PickupKeys = PickupKeys< + Context['messages'] + >, + Message = string +>( + context: Context, + key: Key | ResourceKeys | number | MessageFunction, list: unknown[] ): MessageType | number -export function translate( - context: CoreTranslationContext, - key: Path | number, + +export function translate< + Context extends CoreContext, + Key extends string = string, + ResourceKeys extends PickupKeys = PickupKeys< + Context['messages'] + >, + Message = string +>( + context: Context, + key: Key | ResourceKeys | number | MessageFunction, list: unknown[], plural: number ): MessageType | number -export function translate( - context: CoreTranslationContext, - key: Path | number, + +export function translate< + Context extends CoreContext, + Key extends string = string, + ResourceKeys extends PickupKeys = PickupKeys< + Context['messages'] + >, + Message = string +>( + context: Context, + key: Key | ResourceKeys | number | MessageFunction, list: unknown[], defaultMsg: string ): MessageType | number -export function translate( - context: CoreTranslationContext, - key: Path | number, - list: unknown[], - options: TranslateOptions -): MessageType | number -export function translate( - context: CoreTranslationContext, - message: MessageFunction | string, + +export function translate< + Context extends CoreContext, + Key extends string = string, + ResourceKeys extends PickupKeys = PickupKeys< + Context['messages'] + >, + Message = string +>( + context: Context, + key: Key | ResourceKeys | number | MessageFunction, list: unknown[], - options: TranslateOptions + options: TranslateOptions ): MessageType | number -export function translate( - context: CoreTranslationContext, - key: Path | number, + +export function translate< + Context extends CoreContext, + Key extends string = string, + ResourceKeys extends PickupKeys = PickupKeys< + Context['messages'] + >, + Message = string +>( + context: Context, + key: Key | ResourceKeys | number | MessageFunction, named: NamedValue ): MessageType | number -export function translate( - context: CoreTranslationContext, - key: Path | number, + +export function translate< + Context extends CoreContext, + Key extends string = string, + ResourceKeys extends PickupKeys = PickupKeys< + Context['messages'] + >, + Message = string +>( + context: Context, + key: Key | ResourceKeys | number | MessageFunction, named: NamedValue, plural: number ): MessageType | number -export function translate( - context: CoreTranslationContext, - key: Path | number, + +export function translate< + Context extends CoreContext, + Key extends string = string, + ResourceKeys extends PickupKeys = PickupKeys< + Context['messages'] + >, + Message = string +>( + context: Context, + key: Key | ResourceKeys | number | MessageFunction, named: NamedValue, defaultMsg: string ): MessageType | number -export function translate( - context: CoreTranslationContext, - key: Path | number, - named: NamedValue, - options: TranslateOptions -): MessageType | number -export function translate( - context: CoreTranslationContext, - message: MessageFunction | string, + +export function translate< + Context extends CoreContext, + Key extends string = string, + ResourceKeys extends PickupKeys = PickupKeys< + Context['messages'] + >, + Message = string +>( + context: Context, + key: Key | ResourceKeys | number | MessageFunction, named: NamedValue, - options: TranslateOptions + options: TranslateOptions ): MessageType | number -export function translate( - context: CoreTranslationContext, - ...args: unknown[] -): MessageType | number // for internal // implementation of `translate` function -export function translate( - context: CoreTranslationContext, - ...args: unknown[] -): MessageType | number { +export function translate< + Context extends CoreContext, + Message = string +>(context: Context, ...args: unknown[]): MessageType | number { const { fallbackFormat, postTranslation, @@ -303,7 +388,7 @@ export function translate( context, key as string, locale, - fallbackLocale, + fallbackLocale as FallbackLocale, fallbackWarn, missingWarn ) @@ -368,14 +453,14 @@ export function translate( } // evaluate message with context - const ctxOptions = getMessageContextOptions( + const ctxOptions = getMessageContextOptions( context, targetLocale!, message, options ) const msgContext = createMessageContext(ctxOptions) - const messaged = evaluateMessage( + const messaged = evaluateMessage( context, msg as MessageFunction, msgContext @@ -431,7 +516,7 @@ function escapeParams(options: TranslateOptions) { } function resolveMessageFormat( - context: CoreTranslationContext, + context: CoreContext, key: string, locale: Locale, fallbackLocale: FallbackLocale, @@ -439,7 +524,7 @@ function resolveMessageFormat( missingWarn: boolean | RegExp ): [PathValue, Locale | undefined, LocaleMessageValue] { const { messages, onWarn, messageResolver: resolveValue } = context - const locales = getLocaleChain(context, fallbackLocale, locale) + const locales = getLocaleChain(context as any, fallbackLocale, locale) // eslint-disable-line @typescript-eslint/no-explicit-any let message: LocaleMessageValue = {} let targetLocale: Locale | undefined @@ -517,8 +602,8 @@ function resolveMessageFormat( } if (isString(format) || isFunction(format)) break - const missingRet = handleMissing( - context, + const missingRet = handleMissing( + context as any, // eslint-disable-line @typescript-eslint/no-explicit-any key, targetLocale, missingWarn, @@ -534,7 +619,7 @@ function resolveMessageFormat( } function compileMessageFormat( - context: CoreTranslationContext, + context: CoreContext, key: string, targetLocale: string, format: PathValue, @@ -599,7 +684,7 @@ function compileMessageFormat( } function evaluateMessage( - context: CoreTranslationContext, + context: CoreContext, msg: MessageFunction, msgCtx: MessageContext ): MessageType { @@ -677,7 +762,7 @@ export function parseTranslateArgs( } function getCompileOptions( - context: CoreTranslationContext, + context: CoreContext, locale: Locale, key: string, source: string, @@ -719,7 +804,7 @@ function getCompileOptions( } function getMessageContextOptions( - context: CoreTranslationContext, + context: CoreContext, locale: Locale, message: LocaleMessageValue, options: TranslateOptions diff --git a/packages/core-base/src/types/index.ts b/packages/core-base/src/types/index.ts new file mode 100644 index 000000000..a5f17987a --- /dev/null +++ b/packages/core-base/src/types/index.ts @@ -0,0 +1,2 @@ +export * from './utils' +export * from './intl' \ No newline at end of file diff --git a/packages/core-base/src/types.ts b/packages/core-base/src/types/intl.ts similarity index 84% rename from packages/core-base/src/types.ts rename to packages/core-base/src/types/intl.ts index 0252d4ca5..0b76a98ce 100644 --- a/packages/core-base/src/types.ts +++ b/packages/core-base/src/types/intl.ts @@ -1,3 +1,6 @@ +import type { LocaleRecord, UnionToTuple } from './utils' +import type { Locale } from '@intlify/runtime' + /** * datetime */ @@ -25,7 +28,7 @@ export type DateTimeFormatOptions = | Intl.DateTimeFormatOptions | SpecificDateTimeFormatOptions export type DateTimeFormat = { [key: string]: DateTimeFormatOptions } -export type DateTimeFormats = { [locale: string]: DateTimeFormat } +export type DateTimeFormats = LocaleRecord, Schema> /** * number @@ -54,7 +57,7 @@ export type NumberFormatOptions = | SpecificNumberFormatOptions | CurrencyNumberFormatOptions export type NumberFormat = { [key: string]: NumberFormatOptions } -export type NumberFormats = { [locale: string]: NumberFormat } +export type NumberFormats = LocaleRecord, Schema> export type FormattedNumberPartType = | 'currency' @@ -73,7 +76,4 @@ export type FormattedNumberPart = { type: FormattedNumberPartType value: string } -export type NumberFormatToPartsResult = { [index: number]: FormattedNumberPart } -export interface MetaInfo { - [field: string]: unknown -} +export type NumberFormatToPartsResult = { [index: number]: FormattedNumberPart } \ No newline at end of file diff --git a/packages/core-base/src/types/utils.ts b/packages/core-base/src/types/utils.ts new file mode 100644 index 000000000..054f6dc65 --- /dev/null +++ b/packages/core-base/src/types/utils.ts @@ -0,0 +1,129 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import type { LocaleMessage } from '../context' +import type { DateTimeFormat, NumberFormat } from './intl' + +export type IsUnion = T extends B + ? [B] extends [T] + ? false + : true + : never + +// prettier-ignore +export type UnionToIntersection = (U extends any ? (arg: U) => void : never) extends (arg: infer I) => void + ? I + : never + +export type LastInUnion = UnionToIntersection< + U extends unknown ? (x: U) => 0 : never +> extends (x: infer L) => 0 + ? L + : never + +export type UnionToTuple> = [U] extends [never] + ? [] + : [...UnionToTuple>, Last] + +export type LocaleRecord = { + [K in T[number]]: R +} + +export type First = T[0] + +type __ResourcePath = Key extends string + ? T[Key] extends Record + ? + | `${Key}.${__ResourcePath> & + string}` + | `${Key}.${Exclude & string}` + : never + : never +type _ResourcePath = __ResourcePath | keyof T +export type ResourcePath = _ResourcePath extends string | keyof T + ? _ResourcePath + : keyof T + +export type ResourceValue< + T, + P extends ResourcePath +> = P extends `${infer Key}.${infer Rest}` + ? Key extends keyof T + ? Rest extends ResourcePath + ? ResourceValue + : never + : never + : P extends keyof T + ? T[P] + : never + +export type PickupLocales< + T extends Record, + K = keyof T +> = K extends string ? K : never + +export type PickupKeys< + T extends Record, + K = keyof T +> = K extends string ? ResourcePath : never + +type __ResourceFormatPath = Key extends string + ? T[Key] extends Record + ? | `${Key}` + : never + : never +type _ResourceFormatPath = __ResourceFormatPath | keyof T +export type ResourceFormatPath = _ResourceFormatPath extends string | keyof T + ? _ResourceFormatPath + : keyof T + +export type PickupFormatKeys< + T extends Record, + K = keyof T +> = K extends string ? ResourceFormatPath : never + +export type PickupFallbackLocales = T[number] | `${T[number]}!` + +export type FallbackLocales = + | Locales + | Array + | { + [locale in string]: Array>> + } + | false + +// prettier-ignore +type LocaleParamsType = T extends IsUnion + ? T + : T extends string + ? T + : R + +// prettier-ignore +export type SchemaParams = T extends readonly any[] + ? { message: First, datetime: DateTimeFormat, number: NumberFormat } + : T extends { message?: infer M, datetime?: infer D, number?: infer N } + ? { + message: M extends LocaleMessage ? M : LocaleMessage, + datetime: D extends DateTimeFormat ? D : DateTimeFormat, + number: N extends NumberFormat ? N : NumberFormat + } + : { + message: LocaleMessage, + datetime: DateTimeFormat, + number: NumberFormat + } + +// prettier-ignore +export type LocaleParams = T extends IsUnion + ? { messages: T, datetimeFormats: T, numberFormats: T } + : T extends { messages?: infer M, datetimeFormats?: infer D, numberFormats?: infer N } + ? { + messages: LocaleParamsType, + datetimeFormats: LocaleParamsType, + numberFormats: LocaleParamsType + } + : T extends string + ? { messages: T, datetimeFormats: T, numberFormats: T } + : { messages: Default, datetimeFormats: Default, numberFormats: Default } + +/* eslint-enable @typescript-eslint/no-explicit-any */ \ No newline at end of file diff --git a/packages/core-base/test/context.test.ts b/packages/core-base/test/context.test.ts index 0aca2baa5..2a6ef8d85 100644 --- a/packages/core-base/test/context.test.ts +++ b/packages/core-base/test/context.test.ts @@ -176,7 +176,7 @@ describe('escapeParameter', () => { }) describe('getLocaleChain', () => { - let ctx: CoreContext + let ctx: CoreContext beforeEach(() => { ctx = context({}) }) diff --git a/packages/core-base/test/datetime.test.ts b/packages/core-base/test/datetime.test.ts index 30dffd111..355b1bbb9 100644 --- a/packages/core-base/test/datetime.test.ts +++ b/packages/core-base/test/datetime.test.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ +/* eslint-disable @typescript-eslint/no-empty-function, @typescript-eslint/no-explicit-any */ // utils jest.mock('@intlify/shared', () => ({ @@ -22,7 +22,13 @@ import { compileToFunction } from '../src/compile' import type { DateTimeFormats } from '../src/types' -const datetimeFormats: DateTimeFormats = { +type MyDateTimeSchema = { + short: {} // loose schema + long: {} // loose schema +} + +const datetimeFormats: DateTimeFormats = { + // @ts-ignore NOTE: checking fallback tests 'en-US': { short: { // DD/MM/YYYY, hh:mm (AM|PM) @@ -339,7 +345,7 @@ describe('error', () => { }).toThrowError(errorMessages[CoreErrorCodes.INVALID_ISO_DATE_ARGUMENT]) expect(() => { - datetime(ctx, { someObject: true }) + datetime(ctx, { someObject: true } as any) }).toThrowError(errorMessages[CoreErrorCodes.INVALID_ARGUMENT]) expect(() => { @@ -348,4 +354,4 @@ describe('error', () => { }) }) -/* eslint-enable @typescript-eslint/no-empty-function */ +/* eslint-enable @typescript-eslint/no-empty-function, @typescript-eslint/no-explicit-any */ diff --git a/packages/core-base/test/number.test.ts b/packages/core-base/test/number.test.ts index c31c270c0..25611e43f 100644 --- a/packages/core-base/test/number.test.ts +++ b/packages/core-base/test/number.test.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ +/* eslint-disable @typescript-eslint/no-empty-function, @typescript-eslint/no-explicit-any */ // utils jest.mock('@intlify/shared', () => ({ @@ -19,8 +19,17 @@ import { number } from '../src/number' import { CoreErrorCodes, errorMessages } from '../src/errors' import { registerMessageCompiler } from '../src/context' import { compileToFunction } from '../src/compile' +import { NumberFormats } from '../src/types/index' -const numberFormats = { +type MyNumberSchema = { + currency: {} // loose schema + decimal: {} // loose schema + percent: {} // loose schema + numeric: {} // loose schema +} + +const numberFormats: NumberFormats = { + // @ts-ignore NOTE: checking fallback tests 'en-US': { currency: { style: 'currency', @@ -32,6 +41,7 @@ const numberFormats = { useGrouping: false } }, + // @ts-ignore NOTE: checking fallback tests 'ja-JP': { currency: { style: 'currency', @@ -305,9 +315,9 @@ describe('error', () => { numberFormats }) expect(() => { - number(ctx, '111') + number(ctx, '111' as any) }).toThrowError(errorMessages[CoreErrorCodes.INVALID_ARGUMENT]) }) }) -/* eslint-enable @typescript-eslint/no-empty-function */ +/* eslint-enable @typescript-eslint/no-empty-function, @typescript-eslint/no-explicit-any */ diff --git a/packages/core-base/test/translate.test.ts b/packages/core-base/test/translate.test.ts index af311ae38..23c4922f2 100644 --- a/packages/core-base/test/translate.test.ts +++ b/packages/core-base/test/translate.test.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ +/* eslint-disable @typescript-eslint/no-empty-function, @typescript-eslint/no-explicit-any */ // utils jest.mock('@intlify/shared', () => ({ @@ -34,7 +34,7 @@ describe('features', () => { const ctx = context({ locale: 'en', messages: { - en: { hi: 'hi {0} !' } + en: { hi: 'hi {0} !', nest: { foo: '' } } } }) expect(translate(ctx, 'hi', ['kazupon'])).toEqual('hi kazupon !') @@ -662,7 +662,7 @@ describe('error', () => { } }) expect(() => { - translate(ctx, {}) + translate(ctx, {} as any) }).toThrowError(errorMessages[CoreErrorCodes.INVALID_ARGUMENT]) }) }) @@ -745,4 +745,4 @@ describe('edge cases', () => { }) }) -/* eslint-enable @typescript-eslint/no-empty-function */ +/* eslint-enable @typescript-eslint/no-empty-function, @typescript-eslint/no-explicit-any */ diff --git a/packages/vue-i18n/src/composer.ts b/packages/vue-i18n/src/composer.ts index 9736e2326..33ff56de3 100644 --- a/packages/vue-i18n/src/composer.ts +++ b/packages/vue-i18n/src/composer.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { ref, computed, @@ -38,7 +39,8 @@ import { NOT_REOSLVED, handleFlatJson, MessageFunction, - setAdditionalMeta + setAdditionalMeta, + ResourcePath } from '@intlify/core-base' import { VueDevToolsTimelineEvents } from '@intlify/vue-devtools' import { I18nWarnCodes, getWarnMessage } from './warnings' @@ -58,14 +60,10 @@ import type { MessageType, Locale, LocaleMessageValue, + LocaleMessage, LocaleMessages, CoreContext, - CoreCommonContext, - CoreTranslationContext, - CoreDateTimeContext, - CoreNumberContext, CoreMissingHandler, - CoreOptions, LocaleMessageDictionary, PostTranslationHandler, FallbackLocale, @@ -77,7 +75,14 @@ import type { NumberFormats as NumberFormatsType, DateTimeFormat, NumberFormat, - MetaInfo + MetaInfo, + PickupLocales, + PickupKeys, + PickupFormatKeys, + FallbackLocales, + SchemaParams, + LocaleParams, + ResourceValue } from '@intlify/core-base' import type { VueDevToolsEmitter } from '@intlify/vue-devtools' @@ -101,6 +106,10 @@ export const DevToolsMetaSymbol = makeSymbol('__intlifyMeta') /** @VueI18nComposition */ export type VueMessageType = string | VNode + +// TODO: custom locale message as global schema +export interface CustomLocaleMessage extends LocaleMessage {} // eslint-disable-line @typescript-eslint/no-empty-interface + /** @VueI18nComposition */ export type MissingHandler = ( locale: Locale, @@ -116,11 +125,12 @@ export type PreCompileHandler = () => { export interface CustomBlock { locale: Locale - resource: LocaleMessages | LocaleMessageDictionary + resource: LocaleMessages } export type CustomBlocks = Array> +// prettier-ignore /** * Composer Options * @@ -129,7 +139,56 @@ export type CustomBlocks = Array> * * @VueI18nComposition */ -export interface ComposerOptions { +export interface ComposerOptions< + Message = VueMessageType, + Schema extends { + message?: unknown + datetime?: unknown + number?: unknown + } = { + message: LocaleMessage + datetime: DateTimeFormat + number: NumberFormat + }, + Locales extends + | { + messages: unknown + datetimeFormats: unknown + numberFormats: unknown + } + | string = Locale, + MessagesLocales = Locales extends { messages: infer M } + ? M + : Locales extends string + ? Locales + : Locale, + DateTimeFormatsLocales = Locales extends { datetimeFormats: infer D } + ? D + : Locales extends string + ? Locales + : Locale, + NumberFormatsLocales = Locales extends { numberFormats: infer N } + ? N + : Locales extends string + ? Locales + : Locale, + MessageSchema = Schema extends { message: infer M } ? M : LocaleMessage, + DateTimeSchema = Schema extends { datetime: infer D } ? D : DateTimeFormat, + NumberSchema = Schema extends { number: infer N } ? N : NumberFormat, + _Messages extends LocaleMessages< + MessageSchema, + MessagesLocales, + Message + > = LocaleMessages, + _DateTimeFormats extends DateTimeFormatsType< + DateTimeSchema, + DateTimeFormatsLocales + > = DateTimeFormatsType, + _NumberFormats extends NumberFormatsType< + NumberSchema, + NumberFormatsLocales + > = NumberFormatsType +> { /** * @remarks * The locale of localization. @@ -171,7 +230,7 @@ export interface ComposerOptions { * * @defaultValue `{}` */ - messages?: LocaleMessages + messages?: { [K in keyof _Messages]: MessageSchema } /** * @remarks * Allow use flat json messages or not @@ -187,7 +246,7 @@ export interface ComposerOptions { * * @defaultValue `{}` */ - datetimeFormats?: DateTimeFormatsType + datetimeFormats?: { [K in keyof _DateTimeFormats]: DateTimeSchema } /** * @remarks * The number formats of localization. @@ -196,7 +255,7 @@ export interface ComposerOptions { * * @defaultValue `{}` */ - numberFormats?: NumberFormatsType + numberFormats?: { [K in keyof _NumberFormats]: NumberSchema } /** * @remarks * Custom Modifiers for linked messages. @@ -380,150 +439,25 @@ export interface ComposerOptions { * @internal */ export interface ComposerInternalOptions< + Message = VueMessageType, Messages = {}, DateTimeFormats = {}, - NumberFormats = {}, - Message = VueMessageType + NumberFormats = {} > { __i18n?: CustomBlocks __i18nGlobal?: CustomBlocks - __root?: Composer + __root?: Composer } /** - * Composer interfaces + * Locale message translation functions * * @remarks - * This is the interface for being used for Vue 3 Composition API. + * This is the interface for {@link Composer} * * @VueI18nComposition */ -export interface Composer< - Messages = {}, - DateTimeFormats = {}, - NumberFormats = {}, - Message = VueMessageType -> { - /** - * @remarks - * Instance ID. - */ - id: number - /** - * @remarks - * The current locale this Composer instance is using. - * - * If the locale contains a territory and a dialect, this locale contains an implicit fallback. - * - * @VueI18nSee [Scope and Locale Changing](../guide/essentials/scope) - */ - locale: WritableComputedRef - /** - * @remarks - * The current fallback locales this Composer instance is using. - * - * @VueI18nSee [Fallbacking](../guide/essentials/fallback) - */ - fallbackLocale: WritableComputedRef - /** - * @remarks - * Whether inherit the root level locale to the component localization locale. - * - * @VueI18nSee [Local Scope](../guide/essentials/scope#local-scope-2) - */ - inheritLocale: boolean - /** - * @remarks - * The list of available locales in `messages` in lexical order. - */ - readonly availableLocales: Locale[] - /** - * @remarks - * The locale messages of localization. - * - * @VueI18nSee [Getting Started](../guide/) - */ - readonly messages: ComputedRef - /** - * @remarks - * The datetime formats of localization. - * - * @VueI18nSee [Datetime Formatting](../guide/essentials/datetime) - */ - readonly datetimeFormats: ComputedRef - /** - * @remarks - * The number formats of localization. - * - * @VueI18nSee [Number Formatting](../guide/essentials/number) - */ - readonly numberFormats: ComputedRef - /** - * @remarks - * Custom Modifiers for linked messages. - * - * @VueI18nSee [Custom Modifiers](../guide/essentials/syntax#custom-modifiers) - */ - readonly modifiers: LinkedModifiers - /** - * @remarks - * A set of rules for word pluralization - * - * @VueI18nSee [Custom Pluralization](../guide/essentials/pluralization#custom-pluralization) - */ - readonly pluralRules: PluralizationRules - /** - * @remarks - * Whether this composer instance is global or not - */ - readonly isGlobal: boolean - /** - * @remarks - * Whether suppress warnings outputted when localization fails. - * - * @VueI18nSee [Fallbacking](../guide/essentials/fallback) - */ - missingWarn: boolean | RegExp - /** - * @remarks - * Whether suppress fall back warnings when localization fails. - * - * @VueI18nSee [Fallbacking](../guide/essentials/fallback) - */ - fallbackWarn: boolean | RegExp - /** - * @remarks - * Whether to fall back to root level (global scope) localization when localization fails. - * - * @VueI18nSee [Fallbacking](../guide/essentials/fallback) - */ - fallbackRoot: boolean - /** - * @remarks - * Whether suppress warnings when falling back to either `fallbackLocale` or root. - * - * @VueI18nSee [Fallbacking](../guide/essentials/fallback) - */ - fallbackFormat: boolean - /** - * @remarks - * Whether to allow the use locale messages of HTML formatting. - * - * If you set `false`, will check the locale messages on the Composer instance. - * - * If you are specified `true`, a warning will be output at console. - * - * @VueI18nSee [HTML Message](../guide/essentials/syntax#html-message) - * @VueI18nSee [Change `warnHtmlInMessage` option default value](../guide/migration/breaking#change-warnhtmlinmessage-option-default-value) - */ - warnHtmlMessage: boolean - /** - * @remarks - * Whether interpolation parameters are escaped before the message is translated. - * - * @VueI18nSee [HTML Message](../guide/essentials/syntax#html-message) - */ - escapeParameter: boolean +export interface ComposerTranslation { /** * Locale message translation * @@ -540,12 +474,17 @@ export interface Composer< * * @VueI18nSee [Scope and Locale Changing](../guide/essentials/scope) */ - t(key: Path | number): string + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | number + ): string /** * Locale message translation for plurals * * @remarks - * Overloaded `t`. About details, see the [t](composition#t-key) details. + * Overloaded `t`. About details, see the [call signature](composition#key-key-resourcekeys-number-string) details. * * In this overloaded `t`, return a pluralized translation message. * @@ -561,12 +500,19 @@ export interface Composer< * * @VueI18nSee [Pluralization](../guide/essentials/pluralization) */ - t(key: Path | number, plural: number, options?: TranslateOptions): string + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | number, + plural: number, + options?: TranslateOptions + ): string /** * Locale message translation for missing default message * * @remarks - * Overloaded `t`. About details, see the [t](composition#t-key) details. + * Overloaded `t`. About details, see the [call signature](composition#key-key-resourcekeys-number-string) details. * * In this overloaded `t`, if no translation was found, return a default message. * @@ -580,12 +526,19 @@ export interface Composer< * * @returns Translated message */ - t(key: Path | number, defaultMsg: string, options?: TranslateOptions): string + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | number, + defaultMsg: string, + options?: TranslateOptions + ): string /** * Locale message translation for list interpolations * * @remarks - * Overloaded `t`. About details, see the [t](composition#t-key) details. + * Overloaded `t`. About details, see the [call signature](composition#key-key-resourcekeys-number-string) details. * * In this overloaded `t`, the locale messages should contain a `{0}`, `{1}`, … for each placeholder in the list. * @@ -601,12 +554,19 @@ export interface Composer< * * @VueI18nSee [List interpolation](../guide/essentials/syntax#list-interpolation) */ - t(key: Path | number, list: unknown[], options?: TranslateOptions): string + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | number, + list: unknown[], + options?: TranslateOptions + ): string /** * Locale message translation for list interpolations and plurals * * @remarks - * Overloaded `t`. About details, see the [t](composition#t-key) details. + * Overloaded `t`. About details, see the [call signature](composition#key-key-resourcekeys-number-string) details. * * In this overloaded `t`, the locale messages should contain a `{0}`, `{1}`, … for each placeholder in the list, and return a pluralized translation message. * @@ -619,12 +579,19 @@ export interface Composer< * @VueI18nSee [Pluralization](../guide/essentials/pluralization) * @VueI18nSee [List interpolation](../guide/essentials/syntax#list-interpolation) */ - t(key: Path | number, list: unknown[], plural: number): string + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | number, + list: unknown[], + plural: number + ): string /** * Locale message translation for list interpolations and missing default message * * @remarks - * Overloaded `t`. About details, see the [t](composition#t-key) details. + * Overloaded `t`. About details, see the [call signature](composition#key-key-resourcekeys-number-string) details. * * In this overloaded `t`, the locale messages should contain a `{0}`, `{1}`, … for each placeholder in the list, and if no translation was found, return a default message. * @@ -636,12 +603,19 @@ export interface Composer< * * @VueI18nSee [List interpolation](../guide/essentials/syntax#list-interpolation) */ - t(key: Path | number, list: unknown[], defaultMsg: string): string + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | number, + list: unknown[], + defaultMsg: string + ): string /** * Locale message translation for named interpolations * * @remarks - * Overloaded `t`. About details, see the [t](composition#t-key) details. + * Overloaded `t`. About details, see the [call signature](composition#key-key-resourcekeys-number-string) details. * * In this overloaded `t`, for each placeholder x, the locale messages should contain a `{x}` token. * @@ -657,12 +631,19 @@ export interface Composer< * * @VueI18nSee [Named interpolation](../guide/essentials/syntax#named-interpolation) */ - t(key: Path | number, named: NamedValue, options?: TranslateOptions): string + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | number, + named: NamedValue, + options?: TranslateOptions + ): string /** * Locale message translation for named interpolations and plurals * * @remarks - * Overloaded `t`. About details, see the [t](composition#t-key) details. + * Overloaded `t`. About details, see the [call signature](composition#key-key-resourcekeys-number-string) details. * * In this overloaded `t`, for each placeholder x, the locale messages should contain a `{x}` token, and return a pluralized translation message. * @@ -675,12 +656,19 @@ export interface Composer< * @VueI18nSee [Pluralization](../guide/essentials/pluralization) * @VueI18nSee [Named interpolation](../guide/essentials/syntax#named-interpolation) */ - t(key: Path | number, named: NamedValue, plural: number): string + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | number, + named: NamedValue, + plural: number + ): string /** * Locale message translation for named interpolations and plurals * * @remarks - * Overloaded `t`. About details, see the [t](composition#t-key) details. + * Overloaded `t`. About details, see the [call signature](composition#key-key-resourcekeys-number-string) details. * * In this overloaded `t`, for each placeholder x, the locale messages should contain a `{x}` token, and if no translation was found, return a default message. * @@ -692,9 +680,28 @@ export interface Composer< * * @VueI18nSee [Named interpolation](../guide/essentials/syntax#named-interpolation) */ - t(key: Path | number, named: NamedValue, defaultMsg: string): string - /** @internal */ - t(...args: unknown[]): string + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | number, + named: NamedValue, + defaultMsg: string + ): string +} + +/** + * Resolve locale message translation functions + * + * @remarks + * This is the interface for {@link Composer} + * + * @VueI18nComposition + */ +export interface ComposerResolveLocaleMessageTranslation< + Message, + Locales = 'en-US' +> { /** * Resolve locale message translation * @@ -717,12 +724,12 @@ export interface Composer< * * @VueI18nSee [Scope and Locale Changing](../guide/essentials/scope) */ - rt(message: MessageFunction | Message): string + (message: MessageFunction | Message): string /** * Resolve locale message translation for plurals * * @remarks - * Overloaded `rt`. About details, see the [rt](composition#rt-message) details. + * Overloaded `rt`. About details, see the [call signature](composition#message-messagefunction-message-message-string) details. * * In this overloaded `rt`, return a pluralized translation message. * @@ -740,16 +747,16 @@ export interface Composer< * * @VueI18nSee [Pluralization](../guide/essentials/pluralization) */ - rt( + ( message: MessageFunction | Message, plural: number, - options?: TranslateOptions + options?: TranslateOptions ): string /** * Resolve locale message translation for list interpolations * * @remarks - * Overloaded `rt`. About details, see the [rt](composition#rt-message) details. + * Overloaded `rt`. About details, see the [call signature](composition#message-messagefunction-message-message-string) details. * * In this overloaded `rt`, return a pluralized translation message. * @@ -767,16 +774,16 @@ export interface Composer< * * @VueI18nSee [List interpolation](../guide/essentials/syntax#list-interpolation) */ - rt( + ( message: MessageFunction | Message, list: unknown[], - options?: TranslateOptions + options?: TranslateOptions ): string /** * Resolve locale message translation for named interpolations * * @remarks - * Overloaded `rt`. About details, see the [rt](composition#rt-message) details. + * Overloaded `rt`. About details, see the [call signature](composition#message-messagefunction-message-message-string) details. * * In this overloaded `rt`, for each placeholder x, the locale messages should contain a `{x}` token. * @@ -794,13 +801,25 @@ export interface Composer< * * @VueI18nSee [Named interpolation](../guide/essentials/syntax#named-interpolation) */ - rt( + ( message: MessageFunction | Message, named: NamedValue, - options?: TranslateOptions + options?: TranslateOptions ): string - /** @internal */ - rt(...args: unknown[]): string +} + +/** + * Datetime formatting functions + * + * @remarks + * This is the interface for {@link Composer} + * + * @VueI18nComposition + */ +export interface ComposerDateTimeFormatting< + DateTimeFormats = {}, + Locales = 'en-US' +> { /** * Datetime formatting * @@ -817,54 +836,71 @@ export interface Composer< * * @VueI18nSee [Datetime formatting](../guide/essentials/datetime) */ - d(value: number | Date | string): string + (value: number | Date | string): string /** * Datetime formatting * * @remarks - * Overloaded `d`. About details, see the [d](composition#d-value) details. + * Overloaded `d`. About details, see the [call signature](composition#value-number-date-string-string) details. * * In this overloaded `d`, format in datetime format for a key registered in datetime formats. * * @param value - A value, timestamp number or `Date` instance or ISO 8601 string - * @param key - A key of datetime formats + * @param keyOrOptions - A key of datetime formats, or additional {@link DateTimeOptions | options} for datetime formatting * * @returns Formatted value */ - d(value: number | Date | string, key: string): string + < + Value extends number | Date | string = number, + Key extends string = string, + ResourceKeys extends PickupFormatKeys = PickupFormatKeys + >( + value: Value, + keyOrOptions: + | Key + | ResourceKeys + | DateTimeOptions + ): string /** * Datetime formatting * * @remarks - * Overloaded `d`. About details, see the [d](composition#d-value) details. + * Overloaded `d`. About details, see the [call signature](composition#value-number-date-string-string) details. * * In this overloaded `d`, format in datetime format for a key registered in datetime formats at target locale * * @param value - A value, timestamp number or `Date` instance or ISO 8601 string - * @param key - A key of datetime formats + * @param keyOrOptions - A key of datetime formats, or additional {@link DateTimeOptions | options} for datetime formatting * @param locale - A locale, it will be used over than global scope or local scope. * * @returns Formatted value */ - d(value: number | Date | string, key: string, locale: Locale): string - /** - * Datetime formatting - * - * @remarks - * Overloaded `d`. About details, see the [d](composition#d-value) details. - * - * You can also suppress the warning, when the formatting missing according to the options. - * - * About details of options, see the {@link DateTimeOptions}. - * - * @param value - A value, timestamp number or `Date` instance or ISO 8601 string - * @param options - Additional {@link DateTimeOptions | options} for datetime formatting - * - * @returns Formatted value - */ - d(value: number | Date | string, options: DateTimeOptions): string - /** @internal */ - d(...args: unknown[]): string + < + Value extends number | Date | string = number, + Key extends string = string, + ResourceKeys extends PickupFormatKeys = PickupFormatKeys + >( + value: Value, + keyOrOptions: + | Key + | ResourceKeys + | DateTimeOptions, + locale: Locales + ): string +} + +/** + * Number formatting functions + * + * @remarks + * This is the interface for {@link Composer} + * + * @VueI18nComposition + */ +export interface ComposerNumberFormatting< + NumberFormats = {}, + Locales = 'en-US' +> { /** * Number Formatting * @@ -881,55 +917,233 @@ export interface Composer< * * @VueI18nSee [Number formatting](../guide/essentials/number) */ - n(value: number): string + (value: number): string /** * Number Formatting * * @remarks - * Overloaded `n`. About details, see the [n](composition#n-value) details. + * Overloaded `n`. About details, see the [call signature](composition#value-number-string) details. * * In this overloaded `n`, format in number format for a key registered in number formats. * * @param value - A number value - * @param key - A key of number formats + * @param keyOrOptions - A key of number formats, or additional {@link NumberOptions | options} for number formatting * * @returns Formatted value */ - n(value: number, key: string): string + < + Key extends string = string, + ResourceKeys extends PickupFormatKeys = PickupFormatKeys + >( + value: number, + keyOrOptions: + | Key + | ResourceKeys + | NumberOptions + ): string /** * Number Formatting * * @remarks - * Overloaded `n`. About details, see the [n](composition#n-value) details. + * Overloaded `n`. About details, see the [call signature](composition#value-number-string) details. * * In this overloaded `n`, format in number format for a key registered in number formats at target locale. * * @param value - A number value - * @param key - A key of number formats + * @param keyOrOptions - A key of number formats, or additional {@link NumberOptions | options} for number formatting * @param locale - A locale, it will be used over than global scope or local scope. * * @returns Formatted value */ - n(value: number, key: string, locale: Locale): string + < + Key extends string = string, + ResourceKeys extends PickupFormatKeys = PickupFormatKeys + >( + value: number, + keyOrOptions: + | Key + | ResourceKeys + | NumberOptions, + locale: Locales + ): string +} + +/** + * Composer interfaces + * + * @remarks + * This is the interface for being used for Vue 3 Composition API. + * + * @VueI18nComposition + */ +export interface Composer< + Message = VueMessageType, + Messages = {}, + DateTimeFormats = {}, + NumberFormats = {}, + OptionLocale = Locale, + ResourceLocales = + | PickupLocales> + | PickupLocales> + | PickupLocales>, + Locales = OptionLocale extends Locale + ? [ResourceLocales] extends [never] + ? Locale + : ResourceLocales + : OptionLocale | ResourceLocales +> { + /** + * @remarks + * Instance ID. + */ + id: number /** + * @remarks + * The current locale this Composer instance is using. * - * Number Formatting + * If the locale contains a territory and a dialect, this locale contains an implicit fallback. * + * @VueI18nSee [Scope and Locale Changing](../guide/essentials/scope) + */ + locale: WritableComputedRef + /** * @remarks - * Overloaded `n`. About details, see the [n](composition#n-value) details. + * The current fallback locales this Composer instance is using. * - * You can also suppress the warning, when the formatting missing according to the options. + * @VueI18nSee [Fallbacking](../guide/essentials/fallback) + */ + fallbackLocale: WritableComputedRef> + /** + * @remarks + * Whether inherit the root level locale to the component localization locale. * - * About details of options, see the {@link NumberOptions}. + * @VueI18nSee [Local Scope](../guide/essentials/scope#local-scope-2) + */ + inheritLocale: boolean + /** + * @remarks + * The list of available locales in `messages` in lexical order. + */ + readonly availableLocales: Locales[] + /** + * @remarks + * The locale messages of localization. * - * @param value - A number value - * @param options - Additional {@link NumberOptions | options} for number formatting + * @VueI18nSee [Getting Started](../guide/) + */ + readonly messages: ComputedRef<{ [K in keyof Messages]: Messages[K] }> + /** + * @remarks + * The datetime formats of localization. * - * @returns Formatted value + * @VueI18nSee [Datetime Formatting](../guide/essentials/datetime) + */ + readonly datetimeFormats: ComputedRef< + { [K in keyof DateTimeFormats]: DateTimeFormats[K] } + > + /** + * @remarks + * The number formats of localization. + * + * @VueI18nSee [Number Formatting](../guide/essentials/number) + */ + readonly numberFormats: ComputedRef< + { [K in keyof NumberFormats]: NumberFormats[K] } + > + /** + * @remarks + * Custom Modifiers for linked messages. + * + * @VueI18nSee [Custom Modifiers](../guide/essentials/syntax#custom-modifiers) */ - n(value: number, options: NumberOptions): string - /** @internal */ - n(...args: unknown[]): string + readonly modifiers: LinkedModifiers + /** + * @remarks + * A set of rules for word pluralization + * + * @VueI18nSee [Custom Pluralization](../guide/essentials/pluralization#custom-pluralization) + */ + readonly pluralRules: PluralizationRules + /** + * @remarks + * Whether this composer instance is global or not + */ + readonly isGlobal: boolean + /** + * @remarks + * Whether suppress warnings outputted when localization fails. + * + * @VueI18nSee [Fallbacking](../guide/essentials/fallback) + */ + missingWarn: boolean | RegExp + /** + * @remarks + * Whether suppress fall back warnings when localization fails. + * + * @VueI18nSee [Fallbacking](../guide/essentials/fallback) + */ + fallbackWarn: boolean | RegExp + /** + * @remarks + * Whether to fall back to root level (global scope) localization when localization fails. + * + * @VueI18nSee [Fallbacking](../guide/essentials/fallback) + */ + fallbackRoot: boolean + /** + * @remarks + * Whether suppress warnings when falling back to either `fallbackLocale` or root. + * + * @VueI18nSee [Fallbacking](../guide/essentials/fallback) + */ + fallbackFormat: boolean + /** + * @remarks + * Whether to allow the use locale messages of HTML formatting. + * + * If you set `false`, will check the locale messages on the Composer instance. + * + * If you are specified `true`, a warning will be output at console. + * + * @VueI18nSee [HTML Message](../guide/essentials/syntax#html-message) + * @VueI18nSee [Change `warnHtmlInMessage` option default value](../guide/migration/breaking#change-warnhtmlinmessage-option-default-value) + */ + warnHtmlMessage: boolean + /** + * @remarks + * Whether interpolation parameters are escaped before the message is translated. + * + * @VueI18nSee [HTML Message](../guide/essentials/syntax#html-message) + */ + escapeParameter: boolean + /** + * Locale message translation + * + * @remarks + * About details functions, See the {@link ComposerTranslation} + */ + t: ComposerTranslation + /** + * Resolve locale message translation + * + * @remarks + * About details functions, See the {@link ComposerResolveLocaleMessageTranslation} + */ + rt: ComposerResolveLocaleMessageTranslation + /** + * Datetime formatting + * + * @remarks + * About details functions, See the {@link ComposerDateTimeFormatting} + */ + d: ComposerDateTimeFormatting + /** + * Number Formatting + * + * @remarks + * About details functions, See the {@link ComposerNumberFormatting} + */ + n: ComposerNumberFormatting /** * Translation locale message exist * @@ -943,7 +1157,13 @@ export interface Composer< * * @returns If found locale message, `true`, else `false` */ - te(key: Path, locale?: Locale): boolean + te< + Str extends string, + Key extends PickupKeys = PickupKeys + >( + key: Str | Key, + locale?: Locales + ): boolean /** * Locale messages getter * @@ -1004,7 +1224,19 @@ export interface Composer< * * @return Locale messages */ - tm(key: Path): LocaleMessageValue | {} + tm< + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Target = NonNullable[Locale], + Return = ResourceKeys extends ResourcePath + ? ResourceValue + : Record + >( + key: Key | ResourceKeys + ): Return /** * Get locale message * @@ -1013,9 +1245,22 @@ export interface Composer< * * @param locale - A target locale * + * @typeParam MessageSchema - The locale message schema, default `never` + * * @returns Locale messages */ - getLocaleMessage(locale: Locale): LocaleMessageDictionary + getLocaleMessage< + MessageSchema extends LocaleMessage = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Return = [MessageSchema] extends [never] + ? NonNullable[Locale] + : MessageSchema + >( + locale: LocaleSchema | Locale + ): Return /** * Set locale message * @@ -1024,10 +1269,21 @@ export interface Composer< * * @param locale - A target locale * @param message - A message - */ - setLocaleMessage( - locale: Locale, - message: LocaleMessageDictionary + * + * @typeParam MessageSchema - The locale message schema, default `never` + */ + setLocaleMessage< + MessageSchema extends LocaleMessage = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Message = [MessageSchema] extends [never] + ? NonNullable[Locale] + : MessageSchema + >( + locale: LocaleSchema | Locale, + message: Message ): void /** * Merge locale message @@ -1037,10 +1293,21 @@ export interface Composer< * * @param locale - A target locale * @param message - A message - */ - mergeLocaleMessage( - locale: Locale, - message: LocaleMessageDictionary + * + * @typeParam MessageSchema - The locale message schema, default `never` + */ + mergeLocaleMessage< + MessageSchema extends LocaleMessage = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Message = [MessageSchema] extends [never] + ? Record + : MessageSchema + >( + locale: LocaleSchema | Locale, + message: Message ): void /** * Get datetime format @@ -1050,9 +1317,22 @@ export interface Composer< * * @param locale - A target locale * + * @typeParam DateTimeSchema - The datetime format schema, default `never` + * * @returns Datetime format */ - getDateTimeFormat(locale: Locale): DateTimeFormat + getDateTimeFormat< + DateTimeSchema extends Record = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Return = [DateTimeSchema] extends [never] + ? NonNullable[Locale] + : DateTimeSchema + >( + locale: LocaleSchema | Locale + ): Return /** * Set datetime format * @@ -1061,8 +1341,22 @@ export interface Composer< * * @param locale - A target locale * @param format - A target datetime format - */ - setDateTimeFormat(locale: Locale, format: DateTimeFormat): void + * + * @typeParam DateTimeSchema - The datetime format schema, default `never` + */ + setDateTimeFormat< + DateTimeSchema extends Record = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Formats = [DateTimeSchema] extends [never] + ? NonNullable[Locale] + : DateTimeSchema + >( + locale: LocaleSchema | Locale, + format: Formats + ): void /** * Merge datetime format * @@ -1071,8 +1365,22 @@ export interface Composer< * * @param locale - A target locale * @param format - A target datetime format - */ - mergeDateTimeFormat(locale: Locale, format: DateTimeFormat): void + * + * @typeParam DateTimeSchema - The datetime format schema, default `never` + */ + mergeDateTimeFormat< + DateTimeSchema extends Record = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Formats = [DateTimeSchema] extends [never] + ? Record + : DateTimeSchema + >( + locale: LocaleSchema | Locale, + format: Formats + ): void /** * Get number format * @@ -1081,9 +1389,22 @@ export interface Composer< * * @param locale - A target locale * + * @typeParam NumberSchema - The number format schema, default `never` + * * @returns Number format */ - getNumberFormat(locale: Locale): NumberFormat + getNumberFormat< + NumberSchema extends Record = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Return = [NumberSchema] extends [never] + ? NonNullable[Locale] + : NumberSchema + >( + locale: LocaleSchema | Locale + ): Return /** * Set number format * @@ -1092,8 +1413,22 @@ export interface Composer< * * @param locale - A target locale * @param format - A target number format - */ - setNumberFormat(locale: Locale, format: NumberFormat): void + * + * @typeParam NumberSchema - The number format schema, default `never` + */ + setNumberFormat< + NumberSchema extends Record = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Formats = [NumberSchema] extends [never] + ? NonNullable[Locale] + : NumberSchema + >( + locale: LocaleSchema | Locale, + format: Formats + ): void /** * Merge number format * @@ -1102,8 +1437,22 @@ export interface Composer< * * @param locale - A target locale * @param format - A target number format - */ - mergeNumberFormat(locale: Locale, format: NumberFormat): void + * + * @typeParam NumberSchema - The number format schema, default `never` + */ + mergeNumberFormat< + NumberSchema extends Record = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Formats = [NumberSchema] extends [never] + ? Record + : NumberSchema + >( + locale: LocaleSchema | Locale, + format: Formats + ): void /** * Get post translation handler * @@ -1156,30 +1505,28 @@ type ComposerWarnType = 'translate' | 'number format' | 'datetime format' let composerID = 0 -function defineCoreMissingHandler( - missing: MissingHandler -): CoreMissingHandler { +function defineCoreMissingHandler(missing: MissingHandler): CoreMissingHandler { return (( - ctx: CoreCommonContext, + ctx: CoreContext, locale: Locale, key: Path, type: string ): string | void => { return missing(locale, key, getCurrentInstance() || undefined, type) - }) as CoreMissingHandler + }) as CoreMissingHandler } -type GetLocaleMessagesOptions = { - messages?: LocaleMessages +type GetLocaleMessagesOptions = { + messages?: { [K in keyof Messages]: Messages[K] } __i18n?: CustomBlocks messageResolver?: MessageResolver flatJson?: boolean } -export function getLocaleMessages( +export function getLocaleMessages( locale: Locale, - options: GetLocaleMessagesOptions -): LocaleMessages { + options: GetLocaleMessagesOptions +): { [K in keyof Messages]: Messages[K] } { const { messages, __i18n, messageResolver, flatJson } = options // prettier-ignore @@ -1210,7 +1557,7 @@ export function getLocaleMessages( } } - return ret + return ret as { [K in keyof Messages]: Messages[K] } } const isNotObjectOrIsArray = (val: unknown) => !isObject(val) || isArray(val) @@ -1244,39 +1591,53 @@ const getMetaInfo = /* #__PURE__*/ (): MetaInfo | null => { : null } +export function createComposer< + Message = VueMessageType, + Options extends ComposerOptions = ComposerOptions, + Messages = Options['messages'] extends object ? Options['messages'] : {}, + DateTimeFormats = Options['datetimeFormats'] extends object + ? Options['datetimeFormats'] + : {}, + NumberFormats = Options['numberFormats'] extends object + ? Options['numberFormats'] + : {} +>(options: Options): Composer + +export function createComposer< + Schema = LocaleMessage, + Locales = 'en-US', + Message = VueMessageType, + Options extends ComposerOptions< + Message, + SchemaParams, + LocaleParams + > = ComposerOptions< + Message, + SchemaParams, + LocaleParams + >, + Messages = Options['messages'] extends object ? Options['messages'] : {}, + DateTimeFormats = Options['datetimeFormats'] extends object + ? Options['datetimeFormats'] + : {}, + NumberFormats = Options['numberFormats'] extends object + ? Options['numberFormats'] + : {} +>(options: Options): Composer + /** * Create composer interface factory * * @internal */ -export function createComposer< - Message = VueMessageType, - Options extends ComposerOptions = object, - Messages extends Record< - keyof Options['messages'], - LocaleMessageDictionary - > = Record>, - DateTimeFormats extends Record< - keyof Options['datetimeFormats'], - DateTimeFormat - > = Record, - NumberFormats extends Record< - keyof Options['numberFormats'], - NumberFormat - > = Record ->( - options: Options = {} as Options -): Composer< - Options['messages'], - Options['datetimeFormats'], - Options['numberFormats'], - Message -> { +export function createComposer( + options: any = {} +): any { const { __root } = options as ComposerInternalOptions< - Messages, - DateTimeFormats, - NumberFormats, - Message + Message, + LocaleMessages>, + DateTimeFormatsType, + NumberFormatsType > const _isGlobal = __root === undefined @@ -1305,8 +1666,11 @@ export function createComposer< : _locale.value ) - const _messages = ref>( - getLocaleMessages(_locale.value, options) + const _messages = ref>>( + getLocaleMessages>>( + _locale.value as Locale, + options + ) ) const _datetimeFormats = ref( @@ -1349,7 +1713,7 @@ export function createComposer< // runtime missing let _missing = isFunction(options.missing) ? options.missing : null let _runtimeMissing = isFunction(options.missing) - ? defineCoreMissingHandler(options.missing) + ? defineCoreMissingHandler(options.missing) : null // postTranslation handler @@ -1376,15 +1740,10 @@ export function createComposer< // runtime context // eslint-disable-next-line prefer-const - let _context: CoreContext + let _context: CoreContext - function getCoreContext(): CoreContext< - Messages, - DateTimeFormats, - NumberFormats, - Message - > { - return createCoreContext({ + function getCoreContext(): CoreContext { + const ctxOptions = { version: VERSION, locale: _locale.value, fallbackLocale: _fallbackLocale.value, @@ -1412,16 +1771,12 @@ export function createComposer< ? ((_context as unknown) as CoreInternalContext).__v_emitter : undefined, __meta: { framework: 'vue' } - } as CoreOptions) as CoreContext< - Messages, - DateTimeFormats, - NumberFormats, - Message - > + } + return createCoreContext(ctxOptions as any) } _context = getCoreContext() - updateFallbackLocale(_context, _locale.value, _fallbackLocale.value) + updateFallbackLocale(_context, _locale.value, _fallbackLocale.value) // track reactivity function trackReactivityValues() { @@ -1454,17 +1809,17 @@ export function createComposer< }) // messages - const messages = computed(() => _messages.value as Messages) + const messages = computed, Message>>( + () => _messages.value as any + ) // datetimeFormats - const datetimeFormats = computed( - () => _datetimeFormats.value as DateTimeFormats + const datetimeFormats = computed( + () => _datetimeFormats.value ) // numberFormats - const numberFormats = computed( - () => _numberFormats.value as NumberFormats - ) + const numberFormats = computed(() => _numberFormats.value) // getPostTranslationHandler function getPostTranslationHandler(): PostTranslationHandler | null { @@ -1473,7 +1828,7 @@ export function createComposer< // setPostTranslationHandler function setPostTranslationHandler( - handler: PostTranslationHandler | null + handler: PostTranslationHandler | null ): void { _postTranslation = handler _context.postTranslation = handler @@ -1570,14 +1925,10 @@ export function createComposer< // t function t(...args: unknown[]): string { return wrapWithDeps( - context => - translate( - context as CoreTranslationContext, - ...args - ), + context => Reflect.apply(translate, null, [context, ...args]) as string, () => parseTranslateArgs(...args), 'translate', - root => root.t(...args), + root => Reflect.apply(root.t, root, [...args]), key => key as string, val => isString(val) ) @@ -1595,14 +1946,10 @@ export function createComposer< // d function d(...args: unknown[]): string { return wrapWithDeps( - context => - datetime( - context as CoreDateTimeContext, - ...args - ), + context => Reflect.apply(datetime, null, [context, ...args]) as string, () => parseDateTimeArgs(...args), 'datetime format', - root => root.d(...args), + root => Reflect.apply(root.d, root, [...args]), () => MISSING_RESOLVE_VALUE, val => isString(val) ) @@ -1611,14 +1958,10 @@ export function createComposer< // n function n(...args: unknown[]): string { return wrapWithDeps( - context => - number( - context as CoreNumberContext, - ...args - ), + context => Reflect.apply(number, null, [context, ...args]) as string, () => parseNumberArgs(...args), 'number format', - root => root.n(...args), + root => Reflect.apply(root.n, root, [...args]), () => MISSING_RESOLVE_VALUE, val => isString(val) ) @@ -1644,10 +1987,13 @@ export function createComposer< return wrapWithDeps( context => { let ret: unknown - const _context = context as CoreTranslationContext + const _context = context as CoreContext< + VNode, + LocaleMessages> + > try { _context.processor = processor - ret = translate(_context, ...args) + ret = Reflect.apply(translate, null, [_context, ...args]) } finally { _context.processor = null } @@ -1665,7 +2011,7 @@ export function createComposer< // numberParts, using for `i18n-n` component function numberParts(...args: unknown[]): string | Intl.NumberFormatPart[] { return wrapWithDeps( - context => number(context as CoreContext, ...args), + context => Reflect.apply(number, null, [context, ...args]), () => parseNumberArgs(...args), 'number format', // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -1680,7 +2026,7 @@ export function createComposer< ...args: unknown[] ): string | Intl.DateTimeFormatPart[] { return wrapWithDeps( - context => datetime(context as CoreContext, ...args), + context => Reflect.apply(datetime, null, [context, ...args]), () => parseDateTimeArgs(...args), 'datetime format', // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -1704,7 +2050,7 @@ export function createComposer< function resolveMessages(key: Path): LocaleMessageValue | null { let messages: LocaleMessageValue | null = null - const locales = getLocaleChain( + const locales = getLocaleChain( _context, _fallbackLocale.value, _locale.value @@ -1763,8 +2109,8 @@ export function createComposer< // setDateTimeFormat function setDateTimeFormat(locale: Locale, format: DateTimeFormat): void { _datetimeFormats.value[locale] = format - _context.datetimeFormats = _datetimeFormats.value as typeof _context.datetimeFormats - clearDateTimeFormat(_context, locale, format) + _context.datetimeFormats = _datetimeFormats.value + clearDateTimeFormat(_context, locale, format) } // mergeDateTimeFormat @@ -1773,8 +2119,8 @@ export function createComposer< _datetimeFormats.value[locale] || {}, format ) - _context.datetimeFormats = _datetimeFormats.value as typeof _context.datetimeFormats - clearDateTimeFormat(_context, locale, format) + _context.datetimeFormats = _datetimeFormats.value + clearDateTimeFormat(_context, locale, format) } // getNumberFormat @@ -1785,8 +2131,8 @@ export function createComposer< // setNumberFormat function setNumberFormat(locale: Locale, format: NumberFormat): void { _numberFormats.value[locale] = format - _context.numberFormats = _numberFormats.value as typeof _context.numberFormats - clearNumberFormat(_context, locale, format) + _context.numberFormats = _numberFormats.value + clearNumberFormat(_context, locale, format) } // mergeNumberFormat @@ -1795,8 +2141,8 @@ export function createComposer< _numberFormats.value[locale] || {}, format ) - _context.numberFormats = _numberFormats.value as typeof _context.numberFormats - clearNumberFormat(_context, locale, format) + _context.numberFormats = _numberFormats.value + clearNumberFormat(_context, locale, format) } // for debug @@ -1808,22 +2154,14 @@ export function createComposer< if (_inheritLocale) { _locale.value = val _context.locale = val - updateFallbackLocale( - _context, - _locale.value, - _fallbackLocale.value - ) + updateFallbackLocale(_context, _locale.value, _fallbackLocale.value) } }) watch(__root.fallbackLocale, (val: FallbackLocale) => { if (_inheritLocale) { _fallbackLocale.value = val _context.fallbackLocale = val - updateFallbackLocale( - _context, - _locale.value, - _fallbackLocale.value - ) + updateFallbackLocale(_context, _locale.value, _fallbackLocale.value) } }) } @@ -1839,13 +2177,9 @@ export function createComposer< set inheritLocale(val: boolean) { _inheritLocale = val if (val && __root) { - _locale.value = __root.locale.value + _locale.value = __root.locale.value as Locale _fallbackLocale.value = __root.fallbackLocale.value - updateFallbackLocale( - _context, - _locale.value, - _fallbackLocale.value - ) + updateFallbackLocale(_context, _locale.value, _fallbackLocale.value) } }, get availableLocales(): Locale[] { @@ -1943,3 +2277,5 @@ export function createComposer< return composer } + +/* eslint-enable @typescript-eslint/no-explicit-any */ diff --git a/packages/vue-i18n/src/devtools.ts b/packages/vue-i18n/src/devtools.ts index 861f18189..2277e3d35 100644 --- a/packages/vue-i18n/src/devtools.ts +++ b/packages/vue-i18n/src/devtools.ts @@ -31,26 +31,13 @@ import type { I18n, I18nInternal } from './i18n' import type { Composer } from './composer' import type { VueI18nInternal } from './legacy' -type _I18n< - Messages, - DateTimeFormats, - NumberFormats, - Legacy extends boolean -> = I18n & I18nInternal +type _I18n = I18n & I18nInternal const VUE_I18N_COMPONENT_TYPES = 'vue-i18n: composer properties' let devtoolsApi: DevtoolsPluginApi -export async function enableDevTools< - Messages, - DateTimeFormats, - NumberFormats, - Legacy extends boolean ->( - app: App, - i18n: _I18n -): Promise { +export async function enableDevTools(app: App, i18n: _I18n): Promise { return new Promise((resolve, reject) => { try { setupDevtoolsPlugin( @@ -167,15 +154,10 @@ function getI18nScopeLable(instance: any): string { ) } -function updateComponentTreeTags< - Messages, - DateTimeFormats, - NumberFormats, - Legacy extends boolean ->( +function updateComponentTreeTags( instance: any, // eslint-disable-line @typescript-eslint/no-explicit-any treeNode: ComponentTreeNode, - i18n: _I18n + i18n: _I18n ): void { // prettier-ignore const global = i18n.mode === 'composition' @@ -285,14 +267,9 @@ function getMessageFunctionDetails(func: any): Record { } } -function registerScope< - Messages, - DateTimeFormats, - NumberFormats, - Legacy extends boolean ->( +function registerScope( payload: HookPayloads[Hooks.GET_INSPECTOR_TREE], - i18n: _I18n + i18n: _I18n ): void { payload.rootNodes.push({ id: 'global', @@ -317,14 +294,9 @@ function registerScope< } } -function getComponentInstance< - Messages, - DateTimeFormats, - NumberFormats, - Legacy extends boolean ->( +function getComponentInstance( nodeId: string, - i18n: _I18n + i18n: _I18n ): ComponentInternalInstance | null { let instance: ComponentInternalInstance | null = null @@ -340,54 +312,35 @@ function getComponentInstance< return instance } -function getComposer< - Messages, - DateTimeFormats, - NumberFormats, - Legacy extends boolean ->( - nodeId: string, - i18n: _I18n -): Composer | null { +function getComposer(nodeId: string, i18n: _I18n): Composer | null { if (nodeId === 'global') { return i18n.mode === 'composition' - ? (i18n.global as Composer) - : ((i18n.global as unknown) as VueI18nInternal< - Messages, - DateTimeFormats, - NumberFormats - >).__composer + ? ((i18n.global as unknown) as Composer) + : ((i18n.global as unknown) as VueI18nInternal).__composer } else { const instance = Array.from(i18n.__instances.values()).find( item => item.id.toString() === nodeId ) if (instance) { return i18n.mode === 'composition' - ? (instance as Composer) - : ((instance as unknown) as VueI18nInternal< - Messages, - DateTimeFormats, - NumberFormats - >).__composer + ? ((instance as unknown) as Composer) + : ((instance as unknown) as VueI18nInternal).__composer } else { return null } } } -function inspectScope< - Messages, - DateTimeFormats, - NumberFormats, - Legacy extends boolean ->( +function inspectScope( payload: HookPayloads[Hooks.GET_INSPECTOR_STATE], - i18n: _I18n + i18n: _I18n // eslint-disable-next-line @typescript-eslint/no-explicit-any ): any { const composer = getComposer(payload.nodeId, i18n) if (composer) { - payload.state = makeScopeInspectState(composer) + // TODO: + // eslint-disable-next-line @typescript-eslint/no-explicit-any + payload.state = makeScopeInspectState(composer as any) } return null } @@ -490,14 +443,9 @@ export function addTimelineEvent( } } -function editScope< - Messages, - DateTimeFormats, - NumberFormats, - Legacy extends boolean ->( +function editScope( payload: HookPayloads[Hooks.EDIT_INSPECTOR_STATE], - i18n: _I18n + i18n: _I18n ): void { const composer = getComposer(payload.nodeId, i18n) if (composer) { diff --git a/packages/vue-i18n/src/directive.ts b/packages/vue-i18n/src/directive.ts index f921f7ef6..ece1f4ff8 100644 --- a/packages/vue-i18n/src/directive.ts +++ b/packages/vue-i18n/src/directive.ts @@ -8,7 +8,7 @@ import type { ComponentInternalInstance } from 'vue' import type { I18n, I18nInternal } from './i18n' -import type { VueI18n, VueI18nInternal } from './legacy' +import type { VueI18nInternal } from './legacy' import type { Composer } from './composer' import type { Locale, TranslateOptions, NamedValue } from '@intlify/core-base' @@ -20,45 +20,18 @@ type VTDirectiveValue = { plural?: number } -function getComposer< - Messages, - DateTimeFormats, - NumberFormats, - Legacy extends boolean ->( - i18n: I18n, +function getComposer( + i18n: I18n, instance: ComponentInternalInstance -): Composer { +): Composer { const i18nInternal = (i18n as unknown) as I18nInternal if (i18n.mode === 'composition') { - return (i18nInternal.__getInstance< - Messages, - DateTimeFormats, - NumberFormats, - Composer - >(instance) || i18n.global) as Composer< - Messages, - DateTimeFormats, - NumberFormats - > + return (i18nInternal.__getInstance(instance) || i18n.global) as Composer } else { - const vueI18n = i18nInternal.__getInstance< - Messages, - DateTimeFormats, - NumberFormats, - VueI18n - >(instance) + const vueI18n = i18nInternal.__getInstance(instance) return vueI18n != null - ? ((vueI18n as unknown) as VueI18nInternal< - Messages, - DateTimeFormats, - NumberFormats - >).__composer - : ((i18n.global as unknown) as VueI18nInternal< - Messages, - DateTimeFormats, - NumberFormats - >).__composer + ? ((vueI18n as unknown) as VueI18nInternal).__composer + : ((i18n.global as unknown) as VueI18nInternal).__composer } } @@ -99,14 +72,7 @@ function getComposer< */ export type TranslationDirective = ObjectDirective -export function vTDirective< - Messages, - DateTimeFormats, - NumberFormats, - Legacy extends boolean ->( - i18n: I18n -): TranslationDirective { +export function vTDirective(i18n: I18n): TranslationDirective { const bind = ( el: HTMLElement, { instance, value, modifiers }: DirectiveBinding @@ -122,7 +88,10 @@ export function vTDirective< } const parsedValue = parseValue(value) - el.textContent = composer.t(...makeParams(parsedValue)) + // el.textContent = composer.t(...makeParams(parsedValue)) + el.textContent = Reflect.apply(composer.t, composer, [ + ...makeParams(parsedValue) + ]) } return { diff --git a/packages/vue-i18n/src/i18n.ts b/packages/vue-i18n/src/i18n.ts index 250254f68..febe9b7f4 100644 --- a/packages/vue-i18n/src/i18n.ts +++ b/packages/vue-i18n/src/i18n.ts @@ -32,9 +32,11 @@ import type { ComponentInternalInstance, ComponentOptions, App } from 'vue' import type { Locale, FallbackLocale, - LocaleMessageDictionary, + LocaleMessage, DateTimeFormat, - NumberFormat + NumberFormat, + SchemaParams, + LocaleParams } from '@intlify/core-base' import type { VueDevToolsEmitter, @@ -42,6 +44,7 @@ import type { } from '@intlify/vue-devtools' import type { VueMessageType, + CustomLocaleMessage, Composer, ComposerOptions, ComposerInternalOptions @@ -65,8 +68,28 @@ declare module '@vue/runtime-core' { * * @VueI18nGeneral */ -export type I18nOptions = I18nAdditionalOptions & - (ComposerOptions | VueI18nOptions) +export type I18nOptions< + Schema extends { + message?: unknown + datetime?: unknown + number?: unknown + } = { + message: LocaleMessage + datetime: DateTimeFormat + number: NumberFormat + }, + Locales extends + | { + messages: unknown + datetimeFormats: unknown + numberFormats: unknown + } + | string = Locale +> = I18nAdditionalOptions & + ( + | ComposerOptions + | VueI18nOptions + ) /** * I18n Additional Options @@ -123,7 +146,8 @@ export interface I18n< Messages = {}, DateTimeFormats = {}, NumberFormats = {}, - Legacy extends boolean = true + OptionLocale = Locale, + Legacy = true > { /** * Vue I18n API mode @@ -134,6 +158,7 @@ export interface I18n< * @defaultValue `'composition'` */ readonly mode: I18nMode + // prettier-ignore /** * The property accessible to the global Composer instance or VueI18n instance * @@ -143,8 +168,10 @@ export interface I18n< * An instance of this property is **global scope***. */ readonly global: Legacy extends true - ? VueI18n - : Composer + ? VueI18n + : Legacy extends false + ? Composer + : unknown /** * Install entry point * @@ -159,25 +186,64 @@ export interface I18n< * * @internal */ -export interface I18nInternal { - __instances: Map +export interface I18nInternal< + Messages = {}, + DateTimeFormats = {}, + NumberFormats = {}, + OptionLocale = Locale +> { + __instances: Map< + ComponentInternalInstance, + | VueI18n< + VueMessageType, + Messages, + DateTimeFormats, + NumberFormats, + OptionLocale + > + | Composer< + VueMessageType, + Messages, + DateTimeFormats, + NumberFormats, + OptionLocale + > + > __getInstance< - Messages, - DateTimeFormats, - NumberFormats, Instance extends - | VueI18n - | Composer + | VueI18n< + VueMessageType, + Messages, + DateTimeFormats, + NumberFormats, + OptionLocale + > + | Composer< + VueMessageType, + Messages, + DateTimeFormats, + NumberFormats, + OptionLocale + > >( component: ComponentInternalInstance ): Instance | null __setInstance< - Messages, - DateTimeFormats, - NumberFormats, Instance extends - | VueI18n - | Composer + | VueI18n< + VueMessageType, + Messages, + DateTimeFormats, + NumberFormats, + OptionLocale + > + | Composer< + VueMessageType, + Messages, + DateTimeFormats, + NumberFormats, + OptionLocale + > >( component: ComponentInternalInstance, instance: Instance @@ -205,7 +271,24 @@ export type I18nScope = 'local' | 'parent' | 'global' * * @VueI18nComposition */ -export type UseI18nOptions = ComposerAdditionalOptions & ComposerOptions +export type UseI18nOptions< + Schema extends { + message?: unknown + datetime?: unknown + number?: unknown + } = { + message: LocaleMessage + datetime: DateTimeFormat + number: NumberFormat + }, + Locales extends + | { + messages: unknown + datetimeFormats: unknown + numberFormats: unknown + } + | string = Locale +> = ComposerAdditionalOptions & ComposerOptions /** * Composer additional options for `useI18n` @@ -221,11 +304,31 @@ export interface ComposerAdditionalOptions { useScope?: I18nScope } +export function createI18n< + Legacy extends boolean = true, + Options extends I18nOptions = I18nOptions, + Messages = Options['messages'] extends object ? Options['messages'] : {}, + DateTimeFormats = Options['datetimeFormats'] extends object + ? Options['datetimeFormats'] + : {}, + NumberFormats = Options['numberFormats'] extends object + ? Options['numberFormats'] + : {}, + OptionLocale = Options['locale'] extends string ? Options['locale'] : Locale, + F = keyof CustomLocaleMessage // eslint-disable-line @typescript-eslint/no-unused-vars +>( + options: Options +): I18n + /** * Vue I18n factory * * @param options - An options, see the {@link I18nOptions} * + * @typeParam Schema - The i18n resources (messages, datetimeFormats, numberFormats) schema, default {@link LocaleMessage} + * @typeParam Locales - The locales of i18n resource schema, default `en-US` + * @typeParam Legacy - Whether legacy mode is enabled or disabled, default `true` + * * @returns {@link I18n} instance * * @remarks @@ -296,30 +399,28 @@ export interface ComposerAdditionalOptions { * @VueI18nGeneral */ export function createI18n< - Options extends I18nOptions = {}, - Messages extends Record< - keyof Options['messages'], - LocaleMessageDictionary - > = Record< - keyof Options['messages'], - LocaleMessageDictionary - >, - DateTimeFormats extends Record< - keyof Options['datetimeFormats'], - DateTimeFormat - > = Record, - NumberFormats extends Record< - keyof Options['numberFormats'], - NumberFormat - > = Record + Schema = LocaleMessage, + Locales = 'en-US', + Legacy extends boolean = true, + Options extends I18nOptions< + SchemaParams, + LocaleParams + > = I18nOptions, LocaleParams>, + Messages = Options['messages'] extends object ? Options['messages'] : {}, + DateTimeFormats = Options['datetimeFormats'] extends object + ? Options['datetimeFormats'] + : {}, + NumberFormats = Options['numberFormats'] extends object + ? Options['numberFormats'] + : {}, + OptionLocale = Options['locale'] extends string ? Options['locale'] : Locale, + F = keyof CustomLocaleMessage // eslint-disable-line @typescript-eslint/no-unused-vars >( - options: Options = {} as Options -): I18n< - Options['messages'], - Options['datetimeFormats'], - Options['numberFormats'], - Options['legacy'] extends boolean ? Options['legacy'] : true -> { + options: Options +): I18n + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function createI18n(options: any = {}): any { type _I18n = I18n & I18nInternal // prettier-ignore @@ -327,10 +428,7 @@ export function createI18n< ? options.legacy : __FEATURE_LEGACY_API__ const __globalInjection = !!options.globalInjection - const __instances = new Map< - ComponentInternalInstance, - VueI18n | Composer - >() + const __instances = new Map() // prettier-ignore const __global = __FEATURE_LEGACY_API__ && __legacyMode ? createVueI18n(options) @@ -339,14 +437,6 @@ export function createI18n< __DEV__ ? 'vue-i18n' : '' ) - type Legacy = Options['legacy'] extends boolean ? Options['legacy'] : true - // prettier-ignore - type GlobalType = Legacy extends true - ? VueI18n - : Legacy extends false - ? Composer - : VueI18n - const i18n = { // mode get mode(): I18nMode { @@ -355,41 +445,30 @@ export function createI18n< // install plugin async install(app: App, ...options: unknown[]): Promise { if ((__DEV__ || __FEATURE_PROD_VUE_DEVTOOLS__) && !__NODE_JS__) { - app.__VUE_I18N__ = i18n as _I18n + app.__VUE_I18N__ = (i18n as unknown) as _I18n } // setup global provider app.__VUE_I18N_SYMBOL__ = symbol - app.provide(app.__VUE_I18N_SYMBOL__, i18n as I18n) + app.provide(app.__VUE_I18N_SYMBOL__, (i18n as unknown) as I18n) // global method and properties injection for Composition API if (!__legacyMode && __globalInjection) { - injectGlobalFields( - app, - i18n.global as Composer - ) + injectGlobalFields(app, i18n.global as Composer) } // install built-in components and directive if (__FEATURE_FULL_INSTALL__) { - apply( - app, - i18n as I18n, - ...options - ) + apply(app, i18n as I18n, ...options) } // setup mixin for Legacy API if (__FEATURE_LEGACY_API__ && __legacyMode) { app.mixin( - defineMixin( - __global as VueI18n, - ((__global as unknown) as VueI18nInternal< - Messages, - DateTimeFormats, - NumberFormats - >).__composer as Composer, - i18n as I18nInternal + defineMixin( + (__global as unknown) as VueI18n, + ((__global as unknown) as VueI18nInternal).__composer as Composer, + (i18n as unknown) as I18nInternal ) ) } @@ -402,11 +481,7 @@ export function createI18n< } const emitter: VueDevToolsEmitter = createEmitter() if (__legacyMode) { - const _vueI18n = (__global as unknown) as VueI18nInternal< - Messages, - DateTimeFormats, - NumberFormats - > + const _vueI18n = (__global as unknown) as VueI18nInternal _vueI18n.__enableEmitter && _vueI18n.__enableEmitter(emitter) } else { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -417,23 +492,22 @@ export function createI18n< } }, // global accessor - get global(): GlobalType { - return __global as GlobalType + get global() { + return __global }, // @internal __instances, // @internal - __getInstance< - M extends Messages, - Instance extends VueI18n | Composer - >(component: ComponentInternalInstance): Instance | null { + __getInstance( + component: ComponentInternalInstance + ): Instance | null { return ((__instances.get(component) as unknown) as Instance) || null }, // @internal - __setInstance< - M extends Messages, - Instance extends VueI18n | Composer - >(component: ComponentInternalInstance, instance: Instance): void { + __setInstance( + component: ComponentInternalInstance, + instance: Instance + ): void { __instances.set(component, instance) }, // @internal @@ -442,14 +516,30 @@ export function createI18n< } } - return i18n as I18n + return i18n } +export function useI18n< + Options extends UseI18nOptions = UseI18nOptions, + F = keyof CustomLocaleMessage // eslint-disable-line @typescript-eslint/no-unused-vars +>( + options?: Options +): Composer< + VueMessageType, + NonNullable, + NonNullable, + NonNullable, + NonNullable +> + /** * Use Composition API for Vue I18n * * @param options - An options, see {@link UseI18nOptions} * + * @typeParam Schema - The i18n resources (messages, datetimeFormats, numberFormats) schema, default {@link LocaleMessage} + * @typeParam Locales - The locales of i18n resource schema, default `en-US` + * * @returns {@link Composer} instance * * @remarks @@ -496,29 +586,35 @@ export function createI18n< * @VueI18nComposition */ export function useI18n< - Options extends UseI18nOptions = object, - Messages extends Record< - keyof Options['messages'], - LocaleMessageDictionary - > = Record< - keyof Options['messages'], - LocaleMessageDictionary + Schema = LocaleMessage, + Locales = 'en-US', + Options extends UseI18nOptions< + SchemaParams, + LocaleParams + > = UseI18nOptions< + SchemaParams, + LocaleParams >, - DateTimeFormats extends Record< - keyof Options['datetimeFormats'], - DateTimeFormat - > = Record, - NumberFormats extends Record< - keyof Options['numberFormats'], - NumberFormat - > = Record + F = keyof CustomLocaleMessage // eslint-disable-line @typescript-eslint/no-unused-vars >( - options: Options = {} as Options + options?: Options ): Composer< - Options['messages'], - Options['datetimeFormats'], - Options['numberFormats'] -> { + VueMessageType, + 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, + F = keyof CustomLocaleMessage // eslint-disable-line @typescript-eslint/no-unused-vars +>(options: Options = {} as Options) { const instance = getCurrentInstance() if (instance == null) { throw createI18nError(I18nErrorCodes.MUST_BE_CALL_SETUP_TOP) @@ -536,16 +632,8 @@ export function useI18n< // prettier-ignore const global = i18n.mode === 'composition' - ? ((i18n.global as unknown) as Composer< - Messages, - DateTimeFormats, - NumberFormats - >) - : ((i18n.global as unknown) as VueI18nInternal< - Messages, - DateTimeFormats, - NumberFormats - >).__composer + ? ((i18n.global as unknown) as Composer) + : ((i18n.global as unknown) as VueI18nInternal).__composer // prettier-ignore const scope: I18nScope = isEmptyObject(options) @@ -559,7 +647,7 @@ export function useI18n< if (scope === 'global') { let messages = isObject(options.messages) ? options.messages : {} if ('__i18nGlobal' in instance.type) { - messages = getLocaleMessages(global.locale.value, { + messages = getLocaleMessages(global.locale.value as Locale, { messages, __i18n: instance.type.__i18nGlobal }) @@ -568,7 +656,7 @@ export function useI18n< const locales = Object.keys(messages) if (locales.length) { locales.forEach(locale => { - global.mergeLocaleMessage(locale, messages![locale]) + global.mergeLocaleMessage(locale, messages[locale]) }) } // merge datetime formats @@ -589,7 +677,13 @@ export function useI18n< }) } } - return global + return global as Composer< + VueMessageType, + Messages, + DateTimeFormats, + NumberFormats, + OptionLocale + > } if (scope === 'parent') { @@ -598,9 +692,15 @@ export function useI18n< if (__DEV__) { warn(getWarnMessage(I18nWarnCodes.NOT_FOUND_PARENT_SCOPE)) } - composer = global + composer = (global as unknown) as Composer } - return composer + return composer as Composer< + VueMessageType, + Messages, + DateTimeFormats, + NumberFormats, + OptionLocale + > } // scope 'local' case @@ -609,20 +709,11 @@ export function useI18n< } const i18nInternal = (i18n as unknown) as I18nInternal - let composer = i18nInternal.__getInstance< - Messages, - DateTimeFormats, - NumberFormats, - Composer - >(instance) + let composer = i18nInternal.__getInstance(instance) if (composer == null) { const type = instance.type as ComponentOptions - const composerOptions: ComposerOptions & - ComposerInternalOptions< - Messages, - DateTimeFormats, - NumberFormats - > = assign({}, options) + const composerOptions = assign({}, options) as ComposerOptions & + ComposerInternalOptions if (type.__i18n) { composerOptions.__i18n = type.__i18n @@ -632,64 +723,36 @@ export function useI18n< composerOptions.__root = global } - composer = createComposer(composerOptions) as Composer< - Messages, - DateTimeFormats, - NumberFormats - > - setupLifeCycle( - i18nInternal, - instance, - composer - ) + composer = createComposer(composerOptions) as Composer + setupLifeCycle(i18nInternal, instance, composer) - i18nInternal.__setInstance< - Messages, - DateTimeFormats, - NumberFormats, - Composer - >(instance, composer) + i18nInternal.__setInstance(instance, composer) } - return composer as Composer + return composer as Composer< + VueMessageType, + Messages, + DateTimeFormats, + NumberFormats, + OptionLocale + > } -function getComposer< - Messages, - DateTimeFormats, - NumberFormats, - Legacy extends boolean ->( - i18n: I18n, +function getComposer( + i18n: I18n, target: ComponentInternalInstance -): Composer | null { - let composer: Composer | null = null +): Composer | null { + let composer: Composer | null = null const root = target.root let current: ComponentInternalInstance | null = target.parent while (current != null) { const i18nInternal = (i18n as unknown) as I18nInternal if (i18n.mode === 'composition') { - composer = i18nInternal.__getInstance< - Messages, - DateTimeFormats, - NumberFormats, - Composer - >(current) + composer = i18nInternal.__getInstance(current) } else { - const vueI18n = i18nInternal.__getInstance< - Messages, - DateTimeFormats, - NumberFormats, - VueI18n - >(current) + const vueI18n = i18nInternal.__getInstance(current) if (vueI18n != null) { - composer = (vueI18n as VueI18n< - Messages, - DateTimeFormats, - NumberFormats - > & - VueI18nInternal) - .__composer as Composer + composer = (vueI18n as VueI18n & VueI18nInternal).__composer as Composer } } if (composer != null) { @@ -703,10 +766,10 @@ function getComposer< return composer } -function setupLifeCycle( +function setupLifeCycle( i18n: I18nInternal, target: ComponentInternalInstance, - composer: Composer + composer: Composer ): void { let emitter: VueDevToolsEmitter | null = null @@ -783,10 +846,7 @@ const globalExportProps = [ ] as const const globalExportMethods = ['t', 'rt', 'd', 'n', 'tm'] as const -function injectGlobalFields( - app: App, - composer: Composer -): void { +function injectGlobalFields(app: App, composer: Composer): void { const i18n = Object.create(null) globalExportProps.forEach(prop => { const desc = Object.getOwnPropertyDescriptor(composer, prop) diff --git a/packages/vue-i18n/src/index.ts b/packages/vue-i18n/src/index.ts index 4230311e4..f42e772c8 100644 --- a/packages/vue-i18n/src/index.ts +++ b/packages/vue-i18n/src/index.ts @@ -14,7 +14,7 @@ export { FallbackLocale, LocaleMessageValue, LocaleMessageDictionary, - LocaleMessageArray, + LocaleMessageType, LocaleMessages, NumberFormat as IntlNumberFormat, DateTimeFormat as IntlDateTimeFormat, @@ -33,11 +33,16 @@ export { } from '@intlify/core-base' export { VueMessageType, + CustomLocaleMessage, MissingHandler, ComposerOptions, Composer, CustomBlock, - CustomBlocks + CustomBlocks, + ComposerTranslation, + ComposerDateTimeFormatting, + ComposerNumberFormatting, + ComposerResolveLocaleMessageTranslation } from './composer' export { TranslateResult, @@ -49,7 +54,13 @@ export { NumberFormatResult, Formatter, VueI18nOptions, - VueI18n + VueI18n, + VueI18nTranslation, + VueI18nTranslationChoice, + VueI18nDateTimeFormatting, + VueI18nNumberFormatting, + VueI18nResolveLocaleMessageTranslation, + ComponentInstanceCreatedListener } from './legacy' export { createI18n, diff --git a/packages/vue-i18n/src/legacy.ts b/packages/vue-i18n/src/legacy.ts index e8930ded1..97ccae0de 100644 --- a/packages/vue-i18n/src/legacy.ts +++ b/packages/vue-i18n/src/legacy.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { EnableEmitter, DisableEmitter, createComposer } from './composer' import { I18nWarnCodes, getWarnMessage } from './warnings' import { createI18nError, I18nErrorCodes } from './errors' @@ -20,8 +21,8 @@ import type { PluralizationRules, LinkedModifiers, NamedValue, - MessageFunction, Locale, + LocaleMessage, LocaleMessages, LocaleMessageDictionary, PostTranslationHandler, @@ -31,7 +32,15 @@ import type { DateTimeFormats as DateTimeFormatsType, NumberFormats as NumberFormatsType, DateTimeFormat, - NumberFormat + NumberFormat, + PickupKeys, + PickupFormatKeys, + PickupLocales, + ResourcePath, + ResourceValue, + SchemaParams, + LocaleParams, + FallbackLocales } from '@intlify/core-base' import type { VueDevToolsEmitter } from '@intlify/vue-devtools' import type { @@ -39,7 +48,8 @@ import type { MissingHandler, Composer, ComposerOptions, - ComposerInternalOptions + ComposerInternalOptions, + ComposerResolveLocaleMessageTranslation } from './composer' /** @VueI18nLegacy */ @@ -72,7 +82,30 @@ export type ComponentInstanceCreatedListener = ( * * @VueI18nLegacy */ -export interface VueI18nOptions { +export interface VueI18nOptions< + Message = VueMessageType, + Schema extends { + message?: unknown + datetime?: unknown + number?: unknown + } = { + message: LocaleMessage + datetime: DateTimeFormat + number: NumberFormat + }, + Locales extends + | { + messages: unknown + datetimeFormats: unknown + numberFormats: unknown + } + | string = Locale, + Options extends ComposerOptions = ComposerOptions< + Message, + Schema, + Locales + > +> { /** * @remarks * The locale of localization. @@ -83,7 +116,7 @@ export interface VueI18nOptions { * * @defaultValue `'en-US'` */ - locale?: Locale + locale?: Options['locale'] /** * @remarks * The locale of fallback localization. @@ -94,7 +127,7 @@ export interface VueI18nOptions { * * @defaultValue The default `'en-US'` for the `locale` if it's not specified, or it's `locale` value */ - fallbackLocale?: FallbackLocale + fallbackLocale?: Options['fallbackLocale'] /** * @remarks * The locale messages of localization. @@ -103,14 +136,14 @@ export interface VueI18nOptions { * * @defaultValue `{}` */ - messages?: LocaleMessages + messages?: Options['messages'] /** * @remarks * Allow use flat json messages or not * * @defaultValue `false` */ - flatJson?: boolean + flatJson?: Options['flatJson'] /** * @remarks * The datetime formats of localization. @@ -119,7 +152,7 @@ export interface VueI18nOptions { * * @defaultValue `{}` */ - datetimeFormats?: DateTimeFormatsType + datetimeFormats?: Options['datetimeFormats'] /** * @remarks * The number formats of localization. @@ -128,7 +161,7 @@ export interface VueI18nOptions { * * @defaultValue `{}` */ - numberFormats?: NumberFormatsType + numberFormats?: Options['numberFormats'] /** * @remarks * The list of available locales in messages in lexical order. @@ -142,7 +175,7 @@ export interface VueI18nOptions { * * @VueI18nSee [Custom Modifiers](../guide/essentials/syntax#custom-modifiers) */ - modifiers?: LinkedModifiers + modifiers?: Options['modifiers'] /** * @remarks * The formatter that implemented with Formatter interface. @@ -160,7 +193,7 @@ export interface VueI18nOptions { * * @defaultValue `null` */ - missing?: MissingHandler + missing?: Options['missing'] /** * @remarks * In the component localization, whether to fall back to root level (global scope) localization when localization fails. @@ -171,7 +204,7 @@ export interface VueI18nOptions { * * @defaultValue `true` */ - fallbackRoot?: boolean + fallbackRoot?: Options['fallbackRoot'] /** * @remarks * Whether suppress warnings outputted when localization fails. @@ -184,7 +217,7 @@ export interface VueI18nOptions { * * @defaultValue `false` */ - silentTranslationWarn?: boolean | RegExp + silentTranslationWarn?: Options['missingWarn'] /** * @remarks * Whether do template interpolation on translation keys when your language lacks a translation for a key. @@ -195,7 +228,7 @@ export interface VueI18nOptions { * * @defaultValue `false` */ - silentFallbackWarn?: boolean | RegExp + silentFallbackWarn?: Options['fallbackWarn'] /** * @remarks * Whether suppress warnings when falling back to either `fallbackLocale` or root. @@ -204,7 +237,7 @@ export interface VueI18nOptions { * * @defaultValue `false` */ - formatFallbackMessages?: boolean + formatFallbackMessages?: Options['fallbackFormat'] /** * @remarks * Whether `v-t` directive's element should preserve `textContent` after directive is unbinded. @@ -245,7 +278,7 @@ export interface VueI18nOptions { * * @defaultValue `false` */ - escapeParameterHtml?: boolean + escapeParameterHtml?: Options['escapeParameter'] /** * @remarks * The shared locale messages of localization for components. More detail see Component based localization. @@ -263,7 +296,7 @@ export interface VueI18nOptions { * * @defaultValue `{}` */ - pluralizationRules?: PluralizationRules + pluralizationRules?: Options['pluralRules'] /** * @remarks * A handler for post processing of translation. The handler gets after being called with the `$t`, `t`, `$tc`, and `tc`. @@ -272,7 +305,7 @@ export interface VueI18nOptions { * * @defaultValue `null` */ - postTranslation?: PostTranslationHandler + postTranslation?: Options['postTranslation'] /** * @remarks * Whether synchronize the root level locale to the component localization locale. @@ -361,451 +394,670 @@ export interface VueI18nOptions { } /** - * VueI18n legacy interfaces + * Locale message translation functions for VueI18n legacy interfaces * - * @remarks - * This interface is compatible with interface of `VueI18n` class (offered with Vue I18n v8.x). + * @remarks + * This is the interface for {@link VueI18n} * - * @VueI18nLegacy + * @VueI18nLegacy */ -export interface VueI18n< - Messages = {}, - DateTimeFormats = {}, - NumberFormats = {} -> { - /** - * @remarks - * Instance ID. - */ - id: number +export interface VueI18nTranslation { /** + * Locale message translation. + * * @remarks - * The current locale this VueI18n instance is using. + * If this is used in a reactive context, it will re-evaluate once the locale changes. * - * If the locale contains a territory and a dialect, this locale contains an implicit fallback. + * If [i18n component options](injection#i18n) is specified, it’s translated in preferentially local scope locale messages than global scope locale messages. + * + * If [i18n component options](injection#i18n) isn't specified, it’s translated with global scope locale messages. + * + * @param key - A target locale message key + * + * @returns Translated message * * @VueI18nSee [Scope and Locale Changing](../guide/essentials/scope) */ - locale: Locale + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys + ): TranslateResult /** + * Locale message translation. + * * @remarks - * The current fallback locales this VueI18n instance is using. + * Overloaded `t`. About details, see the [call signature](legacy#key-key-resourcekeys-translateresult) details. * - * @VueI18nSee [Fallbacking](../guide/essentials/fallback) + * @param key - A target locale message key + * @param locale - A locale, it will be used over than global scope or local scope. + * + * @returns Translated message */ - fallbackLocale: FallbackLocale + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys, + locale: Locales | Locale + ): TranslateResult /** + * Locale message translation. + * * @remarks - * The list of available locales in `messages` in lexical order. + * Overloaded `t`. About details, see the [call signature](legacy#key-key-resourcekeys-translateresult) details. + * + * @param key - A target locale message key + * @param locale - A locale, it will be used over than global scope or local scope. + * @param list - A values of list interpolation + * + * @returns Translated message + * + * @VueI18nSee [List interpolation](../guide/essentials/syntax#list-interpolation) */ - readonly availableLocales: Locale[] + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys, + locale: Locales | Locale, + list: unknown[] + ): TranslateResult /** + * Locale message translation. + * * @remarks - * The locale messages of localization. + * Overloaded `t`. About details, see the [call signature](legacy#key-key-resourcekeys-translateresult) details. * - * @VueI18nSee [Getting Started](../guide/) + * @param key - A target locale message key + * @param locale - A locale, it will be used over than global scope or local scope. + * @param named - A values of named interpolation + * + * @returns Translated message + * + * @VueI18nSee [Named interpolation](../guide/essentials/syntax#named-interpolation) */ - readonly messages: Messages + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys, + locale: Locales | Locale, + named: Record + ): TranslateResult /** + * Locale message translation. + * * @remarks - * The datetime formats of localization. + * Overloaded `t`. About details, see the [call signature](legacy#key-key-resourcekeys-translateresult) details. * - * @VueI18nSee [Datetime Formatting](../guide/essentials/datetime) + * @param key - A target locale message key + * @param list - A values of list interpolation + * + * @returns Translated message + * + * @VueI18nSee [List interpolation](../guide/essentials/syntax#list-interpolation) */ - readonly datetimeFormats: DateTimeFormats + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys, + list: unknown[] + ): TranslateResult /** + * Locale message translation. + * * @remarks - * The number formats of localization. + * Overloaded `t`. About details, see the [call signature](legacy#key-key-resourcekeys-translateresult) details. * - * @VueI18nSee [Number Formatting](../guide/essentials/number) + * @param key - A target locale message key + * @param named - A values of named interpolation + * + * @returns Translated message + * + * @VueI18nSee [Named interpolation](../guide/essentials/syntax#named-interpolation) */ - readonly numberFormats: NumberFormats + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys, + named: Record + ): TranslateResult +} + +/** + * Resolve locale message translation functions for VueI18n legacy interfaces + * + * @remarks + * This is the interface for {@link VueI18n}. This interfce is an alias of {@link ComposerResolveLocaleMessageTranslation}. + * + * @VueI18nLegacy + */ +export type VueI18nResolveLocaleMessageTranslation< + Message, + Locales = 'en-US' +> = ComposerResolveLocaleMessageTranslation + +/** + * Locale message pluralization functions for VueI18n legacy interfaces + * + * @remarks + * This is the interface for {@link VueI18n} + * + * @VueI18nLegacy + */ +export interface VueI18nTranslationChoice { /** + * Locale message pluralization + * * @remarks - * Custom Modifiers for linked messages. + * If this is used in a reactive context, it will re-evaluate once the locale changes. * - * @VueI18nSee [Custom Modifiers](../guide/essentials/syntax#custom-modifiers) + * If [i18n component options](injection#i18n) is specified, it’s pluraled in preferentially local scope locale messages than global scope locale messages. + * + * If [i18n component options](injection#i18n) isn't specified, it’s pluraled with global scope locale messages. + * + * The plural choice number is handled with default `1`. + * + * @param key - A target locale message key + * + * @returns Pluraled message + * + * @VueI18nSee [Pluralization](../guide/essentials/pluralization) */ - readonly modifiers: LinkedModifiers + < + Key extends string = string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys + ): TranslateResult /** + * Locale message pluralization + * * @remarks - * The formatter that implemented with Formatter interface. + * Overloaded `tc`. About details, see the [call signature](legacy#key-key-resourcekeys-translateresult-2) details. * - * @deprecated See the [here](../guide/migration/breaking#remove-custom-formatter) + * @param key - A target locale message key + * @param locale - A locale, it will be used over than global scope or local scope. + * + * @returns Pluraled message */ - formatter: Formatter + < + Key extends string = string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys, + locale: Locales | Locale + ): TranslateResult /** + * Locale message pluralization + * * @remarks - * A handler for localization missing. + * Overloaded `tc`. About details, see the [call signature](legacy#key-key-resourcekeys-translateresult-2) details. + * + * @param key - A target locale message key + * @param list - A values of list interpolation + * + * @returns Pluraled message */ - missing: MissingHandler | null + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys, + list: unknown[] + ): TranslateResult /** + * Locale message pluralization + * * @remarks - * A handler for post processing of translation. + * Overloaded `tc`. About details, see the [call signature](legacy#key-key-resourcekeys-translateresult-2) details. + * + * @param key - A target locale message key + * @param named - A values of named interpolation + * + * @returns Pluraled message */ - postTranslation: PostTranslationHandler | null + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys, + named: Record + ): TranslateResult /** + * Locale message pluralization + * * @remarks - * Whether suppress warnings outputted when localization fails. + * Overloaded `tc`. About details, see the [call signature](legacy#key-key-resourcekeys-translateresult-2) details. * - * @VueI18nSee [Fallbacking](../guide/essentials/fallback) + * @param key - A target locale message key + * @param choice - Which plural string to get. 1 returns the first one. + * + * @returns Pluraled message */ - silentTranslationWarn: boolean | RegExp + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys, + choice: number + ): TranslateResult /** + * Locale message pluralization + * * @remarks - * Whether suppress fallback warnings when localization fails. + * Overloaded `tc`. About details, see the [call signature](legacy#key-key-resourcekeys-translateresult-2) details. + * + * @param key - A target locale message key + * @param choice - Which plural string to get. 1 returns the first one. + * @param locale - A locale, it will be used over than global scope or local scope. + * + * @returns Pluraled message */ - silentFallbackWarn: boolean | RegExp + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys, + choice: number, + locale: Locales | Locale + ): TranslateResult /** + * Locale message pluralization + * * @remarks - * Whether suppress warnings when falling back to either `fallbackLocale` or root. + * Overloaded `tc`. About details, see the [call signature](legacy#key-key-resourcekeys-translateresult-2) details. * - * @VueI18nSee [Fallbacking](../guide/essentials/fallback) + * @param key - A target locale message key + * @param choice - Which plural string to get. 1 returns the first one. + * @param list - A values of list interpolation + * + * @returns Pluraled message */ - formatFallbackMessages: boolean + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys, + choice: number, + list: unknown[] + ): TranslateResult /** + * Locale message pluralization + * * @remarks - * Whether synchronize the root level locale to the component localization locale. + * Overloaded `tc`. About details, see the [call signature](legacy#key-key-resourcekeys-translateresult-2) details. * - * @VueI18nSee [Local Scope](../guide/essentials/scope#local-scope-2) + * @param key - A target locale message key + * @param choice - Which plural string to get. 1 returns the first one. + * @param named - A values of named interpolation + * + * @returns Pluraled message */ - sync: boolean + < + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys, + choice: number, + named: Record + ): TranslateResult +} + +/** + * Datetime formatting functions for VueI18n legacy interfaces + * + * @remarks + * This is the interface for {@link VueI18n} + * + * @VueI18nLegacy + */ +export interface VueI18nDateTimeFormatting< + DateTimeFormats = {}, + Locales = 'en-US' +> { /** + * Datetime formatting + * * @remarks - * Whether to allow the use locale messages of HTML formatting. + * If this is used in a reactive context, it will re-evaluate once the locale changes. * - * If you set `warn` or` error`, will check the locale messages on the VueI18n instance. + * If [i18n component options](injection#i18n) is specified, it’s formatted in preferentially local scope datetime formats than global scope locale messages. * - * If you are specified `warn`, a warning will be output at console. + * If [i18n component options](injection#i18n) isn't specified, it’s formatted with global scope datetime formats. * - * If you are specified `error` will occurred an Error. + * @param value - A value, timestamp number or `Date` instance * - * @VueI18nSee [HTML Message](../guide/essentials/syntax#html-message) - * @VueI18nSee [Change `warnHtmlInMessage` option default value](../guide/migration/breaking#change-warnhtmlinmessage-option-default-value) + * @returns Formatted value + * + * @VueI18nSee [Datetime formatting](../guide/essentials/datetime) */ - warnHtmlInMessage: WarnHtmlInMessageLevel + (value: number | Date): DateTimeFormatResult /** + * Datetime formatting + * * @remarks - * Whether interpolation parameters are escaped before the message is translated. + * Overloaded `d`. About details, see the [call signature](legacy#value-number-date-datetimeformatresult) details. * - * @VueI18nSee [HTML Message](../guide/essentials/syntax#html-message) + * @param value - A value, timestamp number or `Date` instance + * @param key - A key of datetime formats + * + * @returns Formatted value */ - escapeParameterHtml: boolean + < + Value extends number | Date = number, + Key extends string = string, + ResourceKeys extends PickupFormatKeys = PickupFormatKeys + >( + value: Value, + key: Key | ResourceKeys + ): DateTimeFormatResult /** + * Datetime formatting + * * @remarks - * Whether `v-t` directive's element should preserve `textContent` after directive is unbinded. + * Overloaded `d`. About details, see the [call signature](legacy#value-number-date-datetimeformatresult) details. * - * @VueI18nSee [Custom Directive](../guide/advanced/directive) - * @VueI18nSee [Remove preserveDirectiveContent option](../guide/migration/breaking#remove-preservedirectivecontent-option) + * @param value - A value, timestamp number or `Date` instance + * @param key - A key of datetime formats + * @param locale - A locale, it will be used over than global scope or local scope. * - * @deprecated The `v-t` directive for Vue 3 now preserves the default content. Therefore, this option and its properties have been removed from the VueI18n instance. + * @returns Formatted value */ - preserveDirectiveContent: boolean + < + Value extends number | Date = number, + Key extends string = string, + ResourceKeys extends PickupFormatKeys = PickupFormatKeys + >( + value: Value, + key: Key | ResourceKeys, + locale: Locales + ): DateTimeFormatResult /** - * A set of rules for word pluralization + * Datetime formatting * - * @VueI18nSee [Custom Pluralization](../guide/essentials/pluralization#custom-pluralization) + * @remarks + * Overloaded `d`. About details, see the [call signature](legacy#value-number-date-datetimeformatresult) details. + * + * @param value - A value, timestamp number or `Date` instance + * @param args - An argument values + * + * @returns Formatted value */ - pluralizationRules: PluralizationRules + (value: number | Date, args: { [key: string]: string }): DateTimeFormatResult +} + +/** + * Number formatting functions for VueI18n legacy interfaces + * + * @remarks + * This is the interface for {@link VueI18n} + * + * @VueI18nLegacy + */ +export interface VueI18nNumberFormatting< + NumberFormats = {}, + Locales = 'en-US' +> { /** - * Locale message translation. + * Number formatting * * @remarks * If this is used in a reactive context, it will re-evaluate once the locale changes. * - * If [i18n component options](injection#i18n) is specified, it’s translated in preferentially local scope locale messages than global scope locale messages. + * If [i18n component options](injection#i18n) is specified, it’s formatted in preferentially local scope number formats than global scope locale messages. * - * If [i18n component options](injection#i18n) isn't specified, it’s translated with global scope locale messages. + * If [i18n component options](injection#i18n) isn't specified, it’s formatted with global scope number formats. * - * @param key - A target locale message key + * @param value - A number value * - * @returns Translated message + * @returns Formatted value * - * @VueI18nSee [Scope and Locale Changing](../guide/essentials/scope) + * @VueI18nSee [Number formatting](../guide/essentials/number) */ - t(key: Path): TranslateResult + (value: number): NumberFormatResult /** - * Locale message translation. + * Number formatting * * @remarks - * Overloaded `t`. About details, see the [t](legacy#t-key) details. + * Overloaded `n`. About details, see the [call signature](legacy#value-number-numberformatresult) details. * - * @param key - A target locale message key - * @param locale - A locale, it will be used over than global scope or local scope. + * @param value - A number value + * @param key - A key of number formats * - * @returns Translated message + * @returns Formatted value */ - t(key: Path, locale: Locale): TranslateResult + < + Key extends string = string, + ResourceKeys extends PickupFormatKeys = PickupFormatKeys + >( + value: number, + key: Key | ResourceKeys + ): NumberFormatResult /** - * Locale message translation. + * Number formatting * * @remarks - * Overloaded `t`. About details, see the [t](legacy#t-key) details. + * Overloaded `n`. About details, see the [call signature](legacy#value-number-numberformatresult) details. * - * @param key - A target locale message key + * @param value - A number value + * @param key - A key of number formats * @param locale - A locale, it will be used over than global scope or local scope. - * @param list - A values of list interpolation - * - * @returns Translated message * - * @VueI18nSee [List interpolation](../guide/essentials/syntax#list-interpolation) + * @returns Formatted value */ - t(key: Path, locale: Locale, list: unknown[]): TranslateResult + < + Key extends string = string, + ResourceKeys extends PickupFormatKeys = PickupFormatKeys + >( + value: number, + key: Key | ResourceKeys, + locale: Locales + ): NumberFormatResult /** - * Locale message translation. + * Number formatting * * @remarks - * Overloaded `t`. About details, see the [t](legacy#t-key) details. + * Overloaded `n`. About details, see the [call signature](legacy#value-number-numberformatresult) details. * - * @param key - A target locale message key - * @param locale - A locale, it will be used over than global scope or local scope. - * @param named - A values of named interpolation + * @param value - A number value + * @param args - An argument values * - * @returns Translated message + * @returns Formatted value + */ + (value: number, args: { [key: string]: string }): NumberFormatResult +} + +/** + * VueI18n legacy interfaces + * + * @remarks + * This interface is compatible with interface of `VueI18n` class (offered with Vue I18n v8.x). + * + * @VueI18nLegacy + */ +export interface VueI18n< + Message = VueMessageType, + Messages = {}, + DateTimeFormats = {}, + NumberFormats = {}, + OptionLocale = Locale, + ResourceLocales = + | PickupLocales> + | PickupLocales> + | PickupLocales>, + Locales = OptionLocale extends string + ? [ResourceLocales] extends [never] + ? Locale + : ResourceLocales + : OptionLocale | ResourceLocales, + Composition extends Composer< + Message, + Messages, + DateTimeFormats, + NumberFormats + > = Composer +> { + /** + * @remarks + * Instance ID. + */ + id: number + /** + * @remarks + * The current locale this VueI18n instance is using. * - * @VueI18nSee [Named interpolation](../guide/essentials/syntax#named-interpolation) + * If the locale contains a territory and a dialect, this locale contains an implicit fallback. + * + * @VueI18nSee [Scope and Locale Changing](../guide/essentials/scope) */ - t(key: Path, locale: Locale, named: object): TranslateResult + // locale: Locales + locale: Locales /** - * Locale message translation. + * @remarks + * The current fallback locales this VueI18n instance is using. + * + * @VueI18nSee [Fallbacking](../guide/essentials/fallback) + */ + fallbackLocale: FallbackLocales + /** + * @remarks + * The list of available locales in `messages` in lexical order. + */ + readonly availableLocales: Composition['availableLocales'] + /** + * @remarks + * The locale messages of localization. * + * @VueI18nSee [Getting Started](../guide/) + */ + readonly messages: { [K in keyof Messages]: Messages[K] } + /** * @remarks - * Overloaded `t`. About details, see the [t](legacy#t-key) details. + * The datetime formats of localization. * - * @param key - A target locale message key - * @param list - A values of list interpolation + * @VueI18nSee [Datetime Formatting](../guide/essentials/datetime) + */ + readonly datetimeFormats: { [K in keyof DateTimeFormats]: DateTimeFormats[K] } + /** + * @remarks + * The number formats of localization. * - * @returns Translated message + * @VueI18nSee [Number Formatting](../guide/essentials/number) + */ + readonly numberFormats: { [K in keyof NumberFormats]: NumberFormats[K] } + /** + * @remarks + * Custom Modifiers for linked messages. * - * @VueI18nSee [List interpolation](../guide/essentials/syntax#list-interpolation) + * @VueI18nSee [Custom Modifiers](../guide/essentials/syntax#custom-modifiers) */ - t(key: Path, list: unknown[]): TranslateResult + readonly modifiers: Composition['modifiers'] /** - * Locale message translation. - * * @remarks - * Overloaded `t`. About details, see the [t](legacy#t-key) details. - * - * @param key - A target locale message key - * @param named - A values of named interpolation - * - * @returns Translated message + * The formatter that implemented with Formatter interface. * - * @VueI18nSee [Named interpolation](../guide/essentials/syntax#named-interpolation) + * @deprecated See the [here](../guide/migration/breaking#remove-custom-formatter) */ - t(key: Path, named: Record): TranslateResult - /** @internal */ - t(...args: unknown[]): TranslateResult // for $t + formatter: Formatter /** - * Resolve locale message translation - * * @remarks - * If this is used in a reactive context, it will re-evaluate once the locale changes. - * - * @VueI18nTip - * The use-case for `rt` is for programmatic locale messages translation with using `tm`, `v-for`, javascript `for` statement. - * - * @VueI18nWarning - * `rt` differs from `t` in that it processes the locale message directly, not the key of the locale message. There is no internal fallback with `rt`. You need to understand and use the structure of the locale messge returned by `tm`. - * - * @param message - A target locale message to be resolved. You will need to specify the locale message returned by `tm`. - * - * @returns Translated message - * - * @VueI18nSee [Scope and Locale Changing](../guide/essentials/scope) + * A handler for localization missing. */ - rt(message: MessageFunction | VueMessageType): string + missing: MissingHandler | null /** - * Resolve locale message translation for plurals - * * @remarks - * Overloaded `rt`. About details, see the [rt](legacy#rt-message) details. - * - * In this overloaded `rt`, return a pluralized translation message. - * - * @VueI18nTip - * The use-case for `rt` is for programmatic locale messages translation with using `tm`, `v-for`, javascript `for` statement. - * - * @VueI18nWarning - * `rt` differs from `t` in that it processes the locale message directly, not the key of the locale message. There is no internal fallback with `rt`. You need to understand and use the structure of the locale messge returned by `tm`. - * - * @param message - A target locale message to be resolved. You will need to specify the locale message returned by `tm`. - * @param plural - Which plural string to get. 1 returns the first one. - * @param options - Additional {@link TranslateOptions | options} for translation - * - * @returns Translated message - * - * @VueI18nSee [Pluralization](../guide/essentials/pluralization) + * A handler for post processing of translation. */ - rt( - message: MessageFunction | VueMessageType, - plural: number, - options?: TranslateOptions - ): string + postTranslation: PostTranslationHandler | null /** - * Resolve locale message translation for list interpolations - * * @remarks - * Overloaded `rt`. About details, see the [rt](legacy#rt-message) details. - * - * In this overloaded `rt`, return a pluralized translation message. - * - * @VueI18nTip - * The use-case for `rt` is for programmatic locale messages translation with using `tm`, `v-for`, javascript `for` statement. - * - * @VueI18nWarning - * `rt` differs from `t` in that it processes the locale message directly, not the key of the locale message. There is no internal fallback with `rt`. You need to understand and use the structure of the locale messge returned by `tm`. - * - * @param message - A target locale message to be resolved. You will need to specify the locale message returned by `tm`. - * @param list - A values of list interpolation. - * @param options - Additional {@link TranslateOptions | options} for translation - * - * @returns Translated message + * Whether suppress warnings outputted when localization fails. * - * @VueI18nSee [List interpolation](../guide/essentials/syntax#list-interpolation) + * @VueI18nSee [Fallbacking](../guide/essentials/fallback) */ - rt( - message: MessageFunction | VueMessageType, - list: unknown[], - options?: TranslateOptions - ): string + silentTranslationWarn: Composition['missingWarn'] /** - * Resolve locale message translation for named interpolations - * * @remarks - * Overloaded `rt`. About details, see the [rt](legacy#rt-message) details. - * - * In this overloaded `rt`, for each placeholder x, the locale messages should contain a `{x}` token. - * - * @VueI18nTip - * The use-case for `rt` is for programmatic locale messages translation with using `tm`, `v-for`, javascript `for` statement. - * - * @VueI18nWarning - * `rt` differs from `t` in that it processes the locale message directly, not the key of the locale message. There is no internal fallback with `rt`. You need to understand and use the structure of the locale messge returned by `tm`. - * - * @param message - A target locale message to be resolved. You will need to specify the locale message returned by `tm`. - * @param named - A values of named interpolation. - * @param options - Additional {@link TranslateOptions | options} for translation - * - * @returns Translated message - * - * @VueI18nSee [Named interpolation](../guide/essentials/syntax#named-interpolation) + * Whether suppress fallback warnings when localization fails. */ - rt( - message: MessageFunction | VueMessageType, - named: NamedValue, - options?: TranslateOptions - ): string - /** @internal */ - rt(...args: unknown[]): string // for $rt + silentFallbackWarn: Composition['fallbackWarn'] /** - * Locale message pluralization - * * @remarks - * If this is used in a reactive context, it will re-evaluate once the locale changes. - * - * If [i18n component options](injection#i18n) is specified, it’s pluraled in preferentially local scope locale messages than global scope locale messages. - * - * If [i18n component options](injection#i18n) isn't specified, it’s pluraled with global scope locale messages. - * - * The plural choice number is handled with default `1`. - * - * @param key - A target locale message key - * - * @returns Pluraled message + * Whether suppress warnings when falling back to either `fallbackLocale` or root. * - * @VueI18nSee [Pluralization](../guide/essentials/pluralization) + * @VueI18nSee [Fallbacking](../guide/essentials/fallback) */ - tc(key: Path): TranslateResult + formatFallbackMessages: Composition['fallbackFormat'] /** - * Locale message pluralization - * * @remarks - * Overloaded `tc`. About details, see the [tc](legacy#tc-key) details. - * - * @param key - A target locale message key - * @param locale - A locale, it will be used over than global scope or local scope. + * Whether synchronize the root level locale to the component localization locale. * - * @returns Pluraled message + * @VueI18nSee [Local Scope](../guide/essentials/scope#local-scope-2) */ - tc(key: Path, locale: Locale): TranslateResult + sync: Composition['inheritLocale'] /** - * Locale message pluralization - * * @remarks - * Overloaded `tc`. About details, see the [tc](legacy#tc-key) details. + * Whether to allow the use locale messages of HTML formatting. * - * @param key - A target locale message key - * @param list - A values of list interpolation + * If you set `warn` or` error`, will check the locale messages on the VueI18n instance. * - * @returns Pluraled message + * If you are specified `warn`, a warning will be output at console. + * + * If you are specified `error` will occurred an Error. + * + * @VueI18nSee [HTML Message](../guide/essentials/syntax#html-message) + * @VueI18nSee [Change `warnHtmlInMessage` option default value](../guide/migration/breaking#change-warnhtmlinmessage-option-default-value) */ - tc(key: Path, list: unknown[]): TranslateResult + warnHtmlInMessage: WarnHtmlInMessageLevel /** - * Locale message pluralization - * * @remarks - * Overloaded `tc`. About details, see the [tc](legacy#tc-key) details. - * - * @param key - A target locale message key - * @param named - A values of named interpolation + * Whether interpolation parameters are escaped before the message is translated. * - * @returns Pluraled message + * @VueI18nSee [HTML Message](../guide/essentials/syntax#html-message) */ - tc(key: Path, named: Record): TranslateResult + escapeParameterHtml: Composition['escapeParameter'] /** - * Locale message pluralization - * * @remarks - * Overloaded `tc`. About details, see the [tc](legacy#tc-key) details. + * Whether `v-t` directive's element should preserve `textContent` after directive is unbinded. * - * @param key - A target locale message key - * @param choice - Which plural string to get. 1 returns the first one. + * @VueI18nSee [Custom Directive](../guide/advanced/directive) + * @VueI18nSee [Remove preserveDirectiveContent option](../guide/migration/breaking#remove-preservedirectivecontent-option) * - * @returns Pluraled message + * @deprecated The `v-t` directive for Vue 3 now preserves the default content. Therefore, this option and its properties have been removed from the VueI18n instance. */ - tc(key: Path, choice: number): TranslateResult + preserveDirectiveContent: boolean /** - * Locale message pluralization - * - * @remarks - * Overloaded `tc`. About details, see the [tc](legacy#tc-key) details. - * - * @param key - A target locale message key - * @param choice - Which plural string to get. 1 returns the first one. - * @param locale - A locale, it will be used over than global scope or local scope. + * A set of rules for word pluralization * - * @returns Pluraled message + * @VueI18nSee [Custom Pluralization](../guide/essentials/pluralization#custom-pluralization) */ - tc(key: Path, choice: number, locale: Locale): TranslateResult + pluralizationRules: Composition['pluralRules'] /** - * Locale message pluralization + * Locale message translation * * @remarks - * Overloaded `tc`. About details, see the [tc](legacy#tc-key) details. - * - * @param key - A target locale message key - * @param choice - Which plural string to get. 1 returns the first one. - * @param list - A values of list interpolation + * About details functions, See the {@link VueI18nTranslation} + */ + t: VueI18nTranslation + /** + * Resolve locale message translation * - * @returns Pluraled message + * @remarks + * About details functions, See the {@link VueI18nResolveLocaleMessageTranslation} */ - tc(key: Path, choice: number, list: unknown[]): TranslateResult + rt: VueI18nResolveLocaleMessageTranslation /** * Locale message pluralization * * @remarks - * Overloaded `tc`. About details, see the [tc](legacy#tc-key) details. - * - * @param key - A target locale message key - * @param choice - Which plural string to get. 1 returns the first one. - * @param named - A values of named interpolation - * - * @returns Pluraled message + * About details functions, See the {@link VueI18nTranslationChoice} */ - tc(key: Path, choice: number, named: Record): TranslateResult - /** @internal */ - tc(...args: unknown[]): TranslateResult // for $tc + tc: VueI18nTranslationChoice /** * Translation locale message exist * @@ -819,7 +1071,13 @@ export interface VueI18n< * * @returns If found locale message, `true`, else `false` */ - te(key: Path, locale?: Locale): boolean + te< + Str extends string, + Key extends PickupKeys = PickupKeys + >( + key: Str | Key, + locale?: Locales + ): boolean /** * Locale messages getter * @@ -874,7 +1132,19 @@ export interface VueI18n< * * @return Locale messages */ - tm(key: Path): LocaleMessageValue | {} + tm< + Key extends string, + ResourceKeys extends PickupKeys = PickupKeys, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Target = NonNullable[Locale], + Return = ResourceKeys extends ResourcePath + ? ResourceValue + : Record + >( + key: Key | ResourceKeys + ): Return /** * Get locale message * @@ -883,9 +1153,22 @@ export interface VueI18n< * * @param locale - A target locale * + * @typeParam MessageSchema - The locale message schema, default `never` + * * @returns Locale messages */ - getLocaleMessage(locale: Locale): LocaleMessageDictionary + getLocaleMessage< + MessageSchema extends LocaleMessage = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Return = [MessageSchema] extends [never] + ? NonNullable[Locale] + : MessageSchema + >( + locale: LocaleSchema | Locale + ): Return /** * Set locale message * @@ -894,10 +1177,21 @@ export interface VueI18n< * * @param locale - A target locale * @param message - A message - */ - setLocaleMessage( - locale: Locale, - message: LocaleMessageDictionary + * + * @typeParam MessageSchema - The locale message schema, default `never` + */ + setLocaleMessage< + MessageSchema extends LocaleMessage = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Message = [MessageSchema] extends [never] + ? NonNullable[Locale] + : MessageSchema + >( + locale: LocaleSchema | Locale, + message: Message ): void /** * Merge locale message @@ -907,67 +1201,29 @@ export interface VueI18n< * * @param locale - A target locale * @param message - A message - */ - mergeLocaleMessage( - locale: Locale, - message: LocaleMessageDictionary - ): void - /** - * Datetime formatting - * - * @remarks - * If this is used in a reactive context, it will re-evaluate once the locale changes. - * - * If [i18n component options](injection#i18n) is specified, it’s formatted in preferentially local scope datetime formats than global scope locale messages. - * - * If [i18n component options](injection#i18n) isn't specified, it’s formatted with global scope datetime formats. - * - * @param value - A value, timestamp number or `Date` instance * - * @returns Formatted value - * - * @VueI18nSee [Datetime formatting](../guide/essentials/datetime) - */ - d(value: number | Date): DateTimeFormatResult - /** - * Datetime formatting - * - * @remarks - * Overloaded `d`. About details, see the [d](legacy#d-value) details. - * - * @param value - A value, timestamp number or `Date` instance - * @param key - A key of datetime formats - * - * @returns Formatted value - */ - d(value: number | Date, key: string): DateTimeFormatResult - /** - * Datetime formatting - * - * @remarks - * Overloaded `d`. About details, see the [d](legacy#d-value) details. - * - * @param value - A value, timestamp number or `Date` instance - * @param key - A key of datetime formats - * @param locale - A locale, it will be used over than global scope or local scope. - * - * @returns Formatted value - */ - d(value: number | Date, key: string, locale: Locale): DateTimeFormatResult + * @typeParam MessageSchema - The locale message schema, default `never` + */ + mergeLocaleMessage< + MessageSchema extends LocaleMessage = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Message = [MessageSchema] extends [never] + ? Record + : MessageSchema + >( + locale: LocaleSchema | Locale, + message: Message + ): void /** * Datetime formatting * * @remarks - * Overloaded `d`. About details, see the [d](legacy#d-value) details. - * - * @param value - A value, timestamp number or `Date` instance - * @param args - An argument values - * - * @returns Formatted value + * About details functions, See the {@link VueI18nDateTimeFormatting} */ - d(value: number | Date, args: { [key: string]: string }): DateTimeFormatResult - /** @internal */ - d(...args: unknown[]): DateTimeFormatResult // for $d + d: VueI18nDateTimeFormatting /** * Get datetime format * @@ -976,9 +1232,22 @@ export interface VueI18n< * * @param locale - A target locale * + * @typeParam DateTimeSchema - The datetime format schema, default `never` + * * @returns Datetime format */ - getDateTimeFormat(locale: Locale): DateTimeFormat + getDateTimeFormat< + DateTimeSchema extends Record = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Return = [DateTimeSchema] extends [never] + ? NonNullable[Locale] + : DateTimeSchema + >( + locale: LocaleSchema | Locale + ): Return /** * Set datetime format * @@ -987,8 +1256,22 @@ export interface VueI18n< * * @param locale - A target locale * @param format - A target datetime format - */ - setDateTimeFormat(locale: Locale, format: DateTimeFormat): void + * + * @typeParam DateTimeSchema - The datetime format schema, default `never` + */ + setDateTimeFormat< + DateTimeSchema extends Record = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Formats = [DateTimeSchema] extends [never] + ? NonNullable[Locale] + : DateTimeSchema + >( + locale: LocaleSchema | Locale, + format: Formats + ): void /** * Merge datetime format * @@ -997,64 +1280,29 @@ export interface VueI18n< * * @param locale - A target locale * @param format - A target datetime format - */ - mergeDateTimeFormat(locale: Locale, format: DateTimeFormat): void - /** - * Number formatting - * - * @remarks - * If this is used in a reactive context, it will re-evaluate once the locale changes. - * - * If [i18n component options](injection#i18n) is specified, it’s formatted in preferentially local scope number formats than global scope locale messages. - * - * If [i18n component options](injection#i18n) isn't specified, it’s formatted with global scope number formats. - * - * @param value - A number value - * - * @returns Formatted value - * - * @VueI18nSee [Number formatting](../guide/essentials/number) - */ - n(value: number): NumberFormatResult - /** - * Number formatting - * - * @remarks - * Overloaded `n`. About details, see the [n](legacy#n-value) details. - * - * @param value - A number value - @param key - A key of number formats - * - * @returns Formatted value - */ - n(value: number, key: string): NumberFormatResult - /** - * Number formatting - * - * @remarks - * Overloaded `n`. About details, see the [n](legacy#n-value) details. - * - * @param value - A number value - * @param key - A key of number formats - * @param locale - A locale, it will be used over than global scope or local scope. * - * @returns Formatted value - */ - n(value: number, key: string, locale: Locale): NumberFormatResult + * @typeParam DateTimeSchema - The datetime format schema, default `never` + */ + mergeDateTimeFormat< + DateTimeSchema extends Record = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Formats = [DateTimeSchema] extends [never] + ? Record + : DateTimeSchema + >( + locale: LocaleSchema | Locale, + format: Formats + ): void /** - * Number formatting + * Number Formatting * * @remarks - * Overloaded `n`. About details, see the [n](legacy#n-value) details. - * - * @param value - A number value - * @param args - An argument values - * - * @returns Formatted value + * About details functions, See the {@link VueI18nNumberFormatting} */ - n(value: number, args: { [key: string]: string }): NumberFormatResult - /** @internal */ - n(...args: unknown[]): NumberFormatResult // for $n + n: VueI18nNumberFormatting /** * Get number format * @@ -1063,9 +1311,22 @@ export interface VueI18n< * * @param locale - A target locale * + * @typeParam NumberSchema - The number format schema, default `never` + * * @returns Number format */ - getNumberFormat(locale: Locale): NumberFormat + getNumberFormat< + NumberSchema extends Record = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Return = [NumberSchema] extends [never] + ? NonNullable[Locale] + : NumberSchema + >( + locale: LocaleSchema | Locale + ): Return /** * Set number format * @@ -1074,8 +1335,22 @@ export interface VueI18n< * * @param locale - A target locale * @param format - A target number format - */ - setNumberFormat(locale: Locale, format: NumberFormat): void + * + * @typeParam NumberSchema - The number format schema, default `never` + */ + setNumberFormat< + NumberSchema extends Record = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Formats = [NumberSchema] extends [never] + ? NonNullable[Locale] + : NumberSchema + >( + locale: LocaleSchema | Locale, + format: Formats + ): void /** * Merge number format * @@ -1084,8 +1359,22 @@ export interface VueI18n< * * @param locale - A target locale * @param format - A target number format - */ - mergeNumberFormat(locale: Locale, format: NumberFormat): void + * + * @typeParam NumberSchema - The number format schema, default `never` + */ + mergeNumberFormat< + NumberSchema extends Record = never, + LocaleSchema extends string = string, + Locale extends PickupLocales> = PickupLocales< + NonNullable + >, + Formats = [NumberSchema] extends [never] + ? Record + : NumberSchema + >( + locale: LocaleSchema | Locale, + format: Formats + ): void /** * Get choice index * @@ -1101,12 +1390,15 @@ export interface VueI18n< * @internal */ export interface VueI18nInternal< + Message = VueMessageType, Messages = {}, DateTimeFormats = {}, NumberFormats = {} > { - __composer: Composer - __onComponentInstanceCreated(target: VueI18n): void + __composer: Composer + __onComponentInstanceCreated( + target: VueI18n + ): void __enableEmitter?: (emitter: VueDevToolsEmitter) => void __disableEmitter?: () => void } @@ -1117,14 +1409,15 @@ export interface VueI18nInternal< * @internal */ function convertComposerOptions< + Message = VueMessageType, Messages = {}, DateTimeFormats = {}, NumberFormats = {} >( - options: VueI18nOptions & - ComposerInternalOptions -): ComposerOptions & - ComposerInternalOptions { + options: VueI18nOptions & + ComposerInternalOptions +): ComposerOptions & + ComposerInternalOptions { const locale = isString(options.locale) ? options.locale : 'en-US' const fallbackLocale = isString(options.fallbackLocale) || @@ -1169,13 +1462,13 @@ function convertComposerOptions< let messages = options.messages if (isPlainObject(options.sharedMessages)) { - const sharedMessages = options.sharedMessages as LocaleMessages + const sharedMessages = options.sharedMessages const locales: Locale[] = Object.keys(sharedMessages) messages = locales.reduce((messages, locale) => { const message = messages[locale] || (messages[locale] = {}) assign(message, sharedMessages[locale]) return messages - }, (messages || {}) as LocaleMessages) as typeof options.messages + }, (messages || {}) as LocaleMessages>) } const { __i18n, __root } = options @@ -1207,38 +1500,51 @@ function convertComposerOptions< } } +export function createVueI18n< + Message = VueMessageType, + Options extends VueI18nOptions = VueI18nOptions, + Messages = Options['messages'] extends object ? Options['messages'] : {}, + DateTimeFormats = Options['datetimeFormats'] extends object + ? Options['datetimeFormats'] + : {}, + NumberFormats = Options['numberFormats'] extends object + ? Options['numberFormats'] + : {} +>(options?: Options): VueI18n + +export function createVueI18n< + Schema = LocaleMessage, + Locales = 'en-US', + Message = VueMessageType, + Options extends VueI18nOptions< + Message, + SchemaParams, + LocaleParams + > = VueI18nOptions< + Message, + SchemaParams, + LocaleParams + >, + Messages = Options['messages'] extends object ? Options['messages'] : {}, + DateTimeFormats = Options['datetimeFormats'] extends object + ? Options['datetimeFormats'] + : {}, + NumberFormats = Options['numberFormats'] extends object + ? Options['numberFormats'] + : {} +>(options?: Options): VueI18n + /** * create VueI18n interface factory * * @internal */ -export function createVueI18n< - Options extends VueI18nOptions = object, - Messages extends Record< - keyof Options['messages'], - LocaleMessageDictionary - > = Record< - keyof Options['messages'], - LocaleMessageDictionary - >, - DateTimeFormats extends Record< - keyof Options['datetimeFormats'], - DateTimeFormat - > = Record, - NumberFormats extends Record< - keyof Options['numberFormats'], - NumberFormat - > = Record ->( - options: Options = {} as Options -): VueI18n< - Options['messages'], - Options['datetimeFormats'], - Options['numberFormats'] -> { - const composer = createComposer( - convertComposerOptions(options) - ) as Composer +export function createVueI18n( + options: any = {} +): any { + const composer = createComposer( + convertComposerOptions(options) + ) as Composer // defines VueI18n const vueI18n = { @@ -1247,38 +1553,38 @@ export function createVueI18n< // locale get locale(): Locale { - return composer.locale.value + return composer.locale.value as Locale }, set locale(val: Locale) { - composer.locale.value = val + composer.locale.value = val as any }, // fallbackLocale get fallbackLocale(): FallbackLocale { - return composer.fallbackLocale.value + return composer.fallbackLocale.value as FallbackLocale }, set fallbackLocale(val: FallbackLocale) { - composer.fallbackLocale.value = val + composer.fallbackLocale.value = val as any }, // messages - get messages(): Messages { + get messages(): LocaleMessages { return composer.messages.value }, // datetimeFormats - get datetimeFormats(): DateTimeFormats { + get datetimeFormats(): DateTimeFormatsType { return composer.datetimeFormats.value }, // numberFormats - get numberFormats(): NumberFormats { + get numberFormats(): NumberFormatsType { return composer.numberFormats.value }, // availableLocales get availableLocales(): Locale[] { - return composer.availableLocales + return composer.availableLocales as Locale[] }, // formatter @@ -1324,7 +1630,7 @@ export function createVueI18n< }, // modifiers - get modifiers(): LinkedModifiers { + get modifiers(): LinkedModifiers { return composer.modifiers }, @@ -1337,12 +1643,10 @@ export function createVueI18n< }, // postTranslation - get postTranslation(): PostTranslationHandler | null { + get postTranslation(): PostTranslationHandler | null { return composer.getPostTranslationHandler() }, - set postTranslation( - handler: PostTranslationHandler | null - ) { + set postTranslation(handler: PostTranslationHandler | null) { composer.setPostTranslationHandler(handler) }, @@ -1415,11 +1719,11 @@ export function createVueI18n< named = arg3 as NamedValue } - return composer.t(key, list || named || {}, options) + return composer.t(key, (list || named || {}) as any, options) }, rt(...args: unknown[]): TranslateResult { - return composer.rt(...args) + return Reflect.apply(composer.rt, composer, [...args]) }, // tc @@ -1452,7 +1756,7 @@ export function createVueI18n< named = arg3 as NamedValue } - return composer.t(key, list || named || {}, options) + return composer.t(key, (list || named || {}) as any, options) }, // te @@ -1483,12 +1787,12 @@ export function createVueI18n< locale: Locale, message: LocaleMessageDictionary ): void { - composer.mergeLocaleMessage(locale, message) + composer.mergeLocaleMessage(locale, message as any) }, // d d(...args: unknown[]): DateTimeFormatResult { - return composer.d(...args) + return Reflect.apply(composer.d, composer, [...args]) }, // getDateTimeFormat @@ -1508,7 +1812,7 @@ export function createVueI18n< // n n(...args: unknown[]): NumberFormatResult { - return composer.n(...args) + return Reflect.apply(composer.n, composer, [...args]) }, // getNumberFormat @@ -1535,25 +1839,23 @@ export function createVueI18n< }, // for internal - __onComponentInstanceCreated(target: VueI18n): void { + __onComponentInstanceCreated(target: VueI18n): void { const { componentInstanceCreatedListener } = options if (componentInstanceCreatedListener) { - componentInstanceCreatedListener(target, vueI18n) + componentInstanceCreatedListener(target, vueI18n) } } } // for vue-devtools timeline event if (__DEV__) { - ;(vueI18n as VueI18nInternal).__enableEmitter = ( + ;(vueI18n as VueI18nInternal).__enableEmitter = ( emitter: VueDevToolsEmitter ): void => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any const __composer = composer as any __composer[EnableEmitter] && __composer[EnableEmitter](emitter) } - ;(vueI18n as VueI18nInternal).__disableEmitter = (): void => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any + ;(vueI18n as VueI18nInternal).__disableEmitter = (): void => { const __composer = composer as any __composer[DisableEmitter] && __composer[DisableEmitter]() } @@ -1561,3 +1863,5 @@ export function createVueI18n< return vueI18n } + +/* eslint-enable @typescript-eslint/no-explicit-any */ diff --git a/packages/vue-i18n/src/mixin.ts b/packages/vue-i18n/src/mixin.ts index c379f60d2..a9ac842e3 100644 --- a/packages/vue-i18n/src/mixin.ts +++ b/packages/vue-i18n/src/mixin.ts @@ -28,9 +28,9 @@ import type { import type { I18nInternal } from './i18n' // supports compatibility for legacy vue-i18n APIs -export function defineMixin( - vuei18n: VueI18n, - composer: Composer, +export function defineMixin( + vuei18n: VueI18n, + composer: Composer, i18n: I18nInternal ): ComponentOptions { return { @@ -44,12 +44,14 @@ export function defineMixin( const options = this.$options if (options.i18n) { const optionsI18n = options.i18n as VueI18nOptions & - ComposerInternalOptions + ComposerInternalOptions + if (options.__i18n) { optionsI18n.__i18n = options.__i18n } optionsI18n.__root = composer if (this === this.$root) { + // TODO; this.$i18n = mergeToRoot(vuei18n, optionsI18n) } else { this.$i18n = createVueI18n(optionsI18n) @@ -59,7 +61,7 @@ export function defineMixin( this.$i18n = mergeToRoot(vuei18n, options) } else { this.$i18n = createVueI18n({ - __i18n: (options as ComposerInternalOptions).__i18n, + __i18n: (options as ComposerInternalOptions).__i18n, __root: composer } as VueI18nOptions) } @@ -68,20 +70,10 @@ export function defineMixin( this.$i18n = vuei18n } - ;((vuei18n as unknown) as VueI18nInternal< - Messages, - DateTimeFormats, - NumberFormats - >).__onComponentInstanceCreated(this.$i18n) - i18n.__setInstance< - Messages, - DateTimeFormats, - NumberFormats, - VueI18n - >( - instance, - this.$i18n as VueI18n + ;((vuei18n as unknown) as VueI18nInternal).__onComponentInstanceCreated( + this.$i18n ) + i18n.__setInstance(instance, this.$i18n as VueI18n) // defines vue-i18n legacy APIs this.$t = (...args: unknown[]): TranslateResult => this.$i18n.t(...args) @@ -102,11 +94,7 @@ export function defineMixin( if ((__DEV__ || __FEATURE_PROD_VUE_DEVTOOLS__) && !__NODE_JS__) { this.$el.__VUE_I18N__ = this.$i18n.__composer const emitter: VueDevToolsEmitter = (this.__v_emitter = createEmitter()) - const _vueI18n = (this.$i18n as unknown) as VueI18nInternal< - Messages, - DateTimeFormats, - NumberFormats - > + const _vueI18n = (this.$i18n as unknown) as VueI18nInternal _vueI18n.__enableEmitter && _vueI18n.__enableEmitter(emitter) emitter.on('*', addTimelineEvent) } @@ -125,11 +113,7 @@ export function defineMixin( this.__v_emitter.off('*', addTimelineEvent) delete this.__v_emitter } - const _vueI18n = (this.$i18n as unknown) as VueI18nInternal< - Messages, - DateTimeFormats, - NumberFormats - > + const _vueI18n = (this.$i18n as unknown) as VueI18nInternal _vueI18n.__disableEmitter && _vueI18n.__disableEmitter() delete this.$el.__VUE_I18N__ } @@ -148,11 +132,10 @@ export function defineMixin( } } -function mergeToRoot( - root: VueI18n, - options: VueI18nOptions & - ComposerInternalOptions -): VueI18n { +function mergeToRoot( + root: VueI18n, + options: VueI18nOptions & ComposerInternalOptions +): VueI18n { root.locale = options.locale || root.locale root.fallbackLocale = options.fallbackLocale || root.fallbackLocale root.missing = options.missing || root.missing @@ -171,11 +154,12 @@ function mergeToRoot( ;(root as any).__composer[SetPluralRulesSymbol]( options.pluralizationRules || root.pluralizationRules ) - const messages = getLocaleMessages(root.locale, { + const messages = getLocaleMessages(root.locale as Locale, { messages: options.messages, __i18n: options.__i18n }) Object.keys(messages).forEach(locale => + // TODO: root.mergeLocaleMessage(locale, messages[locale]) ) if (options.datetimeFormats) { diff --git a/packages/vue-i18n/src/plugin.ts b/packages/vue-i18n/src/plugin.ts index 3fda2375a..d33f0930c 100644 --- a/packages/vue-i18n/src/plugin.ts +++ b/packages/vue-i18n/src/plugin.ts @@ -39,16 +39,7 @@ export interface I18nPluginOptions { globalInstall?: boolean } -export function apply< - Messages, - DateTimeFormats, - NumberFormats, - Legacy extends boolean ->( - app: App, - i18n: I18n, - ...options: unknown[] -): void { +export function apply(app: App, i18n: I18n, ...options: unknown[]): void { const pluginOptions = isPlainObject(options[0]) ? (options[0] as I18nPluginOptions) : {} @@ -76,8 +67,5 @@ export function apply< } // install directive - app.directive( - 't', - vTDirective(i18n) - ) + app.directive('t', vTDirective(i18n)) } diff --git a/packages/vue-i18n/src/runtime.ts b/packages/vue-i18n/src/runtime.ts index c6655e478..f905b33b6 100644 --- a/packages/vue-i18n/src/runtime.ts +++ b/packages/vue-i18n/src/runtime.ts @@ -10,7 +10,7 @@ export { FallbackLocale, LocaleMessageValue, LocaleMessageDictionary, - LocaleMessageArray, + LocaleMessageType, LocaleMessages, NumberFormat as IntlNumberFormat, DateTimeFormat as IntlDateTimeFormat, @@ -32,7 +32,12 @@ export { MissingHandler, ComposerOptions, Composer, - CustomBlocks + CustomBlock, + CustomBlocks, + ComposerTranslation, + ComposerDateTimeFormatting, + ComposerNumberFormatting, + ComposerResolveLocaleMessageTranslation } from './composer' export { TranslateResult, @@ -44,7 +49,13 @@ export { NumberFormatResult, Formatter, VueI18nOptions, - VueI18n + VueI18n, + VueI18nTranslation, + VueI18nTranslationChoice, + VueI18nDateTimeFormatting, + VueI18nNumberFormatting, + VueI18nResolveLocaleMessageTranslation, + ComponentInstanceCreatedListener } from './legacy' export { createI18n, diff --git a/packages/vue-i18n/src/vue.d.ts b/packages/vue-i18n/src/vue.d.ts index 40365cbbc..8e9c1d1d4 100644 --- a/packages/vue-i18n/src/vue.d.ts +++ b/packages/vue-i18n/src/vue.d.ts @@ -6,7 +6,9 @@ import type { MessageFunction, TranslateOptions, DateTimeOptions, - NumberOptions + NumberOptions, + PickupKeys, + PickupFormatKeys } from '@intlify/core' import type { CustomBlocks, VueMessageType } from './composer' import type { @@ -75,7 +77,13 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path): TranslateResult + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path + ): TranslateResult /** * Locale message translation * @@ -87,7 +95,13 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, locale: Locale): TranslateResult + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path + ): TranslateResult /** * Locale message translation * @@ -100,7 +114,15 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, locale: Locale, list: unknown[]): TranslateResult + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + locale: Locale, + list: unknown[] + ): TranslateResult /** * Locale message translation * @@ -113,7 +135,15 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, locale: Locale, named: object): TranslateResult + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + locale: Locale, + named: object + ): TranslateResult /** * Locale message translation * @@ -125,7 +155,14 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, list: unknown[]): TranslateResult + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + list: unknown[] + ): TranslateResult /** * Locale message translation * @@ -137,7 +174,14 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, named: Record): TranslateResult + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + named: Record + ): TranslateResult /** * Locale message translation * @@ -148,7 +192,13 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path): string + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path + ): string /** * Locale message translation * @@ -160,7 +210,14 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, plural: number): string + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + plural: number + ): string /** * Locale message translation * @@ -173,7 +230,15 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, plural: number, options: TranslateOptions): string + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + plural: number, + options: TranslateOptions + ): string /** * Locale message translation * @@ -185,7 +250,14 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, defaultMsg: string): string + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + defaultMsg: string + ): string /** * Locale message translation * @@ -198,7 +270,15 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, defaultMsg: string, options: TranslateOptions): string + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + defaultMsg: string, + options: TranslateOptions + ): string /** * Locale message translation * @@ -210,7 +290,14 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, list: unknown[]): string + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + list: unknown[] + ): string /** * Locale message translation * @@ -223,7 +310,15 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, list: unknown[], plural: number): string + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + list: unknown[], + plural: number + ): string /** * Locale message translation * @@ -236,7 +331,15 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, list: unknown[], defaultMsg: string): string + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + list: unknown[], + defaultMsg: string + ): string /** * Locale message translation * @@ -249,7 +352,15 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, list: unknown[], options: TranslateOptions): string + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + list: unknown[], + options: TranslateOptions + ): string /** * Locale message translation * @@ -261,7 +372,14 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, named: NamedValue): string + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + named: NamedValue + ): string /** * Locale message translation * @@ -274,7 +392,15 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, named: NamedValue, plural: number): string + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + named: NamedValue, + plural: number + ): string /** * Locale message translation * @@ -287,7 +413,15 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, named: NamedValue, defaultMsg: string): string + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + named: NamedValue, + defaultMsg: string + ): string /** * Locale message translation * @@ -300,7 +434,15 @@ declare module '@vue/runtime-core' { * * @returns translation message */ - $t(key: Path, named: NamedValue, options: TranslateOptions): string + $t< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + named: NamedValue, + options: TranslateOptions + ): string /** * Resolve locale message translation * @@ -382,7 +524,13 @@ declare module '@vue/runtime-core' { * * @returns translation message that is pluraled */ - $tc(key: Path): TranslateResult + $tc< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path + ): TranslateResult /** * Locale message pluralization * @@ -395,7 +543,14 @@ declare module '@vue/runtime-core' { * * @returns translation message that is pluraled */ - $tc(key: Path, locale: Locale): TranslateResult + $tc< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + locale: Locale + ): TranslateResult /** * Locale message pluralization * @@ -408,7 +563,14 @@ declare module '@vue/runtime-core' { * * @returns translation message that is pluraled */ - $tc(key: Path, list: unknown[]): TranslateResult + $tc< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + list: unknown[] + ): TranslateResult /** * Locale message pluralization * Supported for Legacy API mode only. @@ -422,7 +584,14 @@ declare module '@vue/runtime-core' { * * @returns translation message that is pluraled */ - $tc(key: Path, named: Record): TranslateResult + $tc< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + named: Record + ): TranslateResult /** * Locale message pluralization * Supported for Legacy API mode only. @@ -436,7 +605,14 @@ declare module '@vue/runtime-core' { * * @returns translation message that is pluraled */ - $tc(key: Path, choice: number): TranslateResult + $tc< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + choice: number + ): TranslateResult /** * Locale message pluralization * Supported for Legacy API mode only. @@ -451,7 +627,15 @@ declare module '@vue/runtime-core' { * * @returns translation message that is pluraled */ - $tc(key: Path, choice: number, locale: Locale): TranslateResult + $tc< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + choice: number, + locale: Locale + ): TranslateResult /** * Locale message pluralization * Supported for Legacy API mode only. @@ -466,7 +650,15 @@ declare module '@vue/runtime-core' { * * @returns translation message that is pluraled */ - $tc(key: Path, choice: number, list: unknown[]): TranslateResult + $tc< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + choice: number, + list: unknown[] + ): TranslateResult /** * Locale message pluralization * Supported for Legacy API mode only. @@ -481,8 +673,12 @@ declare module '@vue/runtime-core' { * * @returns translation message that is pluraled */ - $tc( - key: Path, + $tc< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, choice: number, named: Record ): TranslateResult @@ -498,7 +694,14 @@ declare module '@vue/runtime-core' { * * @returns if found locale message, `true`, else `false` */ - $te(key: Path, locale?: Locale): boolean + $te< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys | Path, + locale?: Locale + ): boolean /** * Datetime formatting * @@ -526,7 +729,15 @@ declare module '@vue/runtime-core' { * * @returns formatted value */ - $d(value: number | Date, key: string): DateTimeFormatResult + $d< + Value extends number | Date = number, + Key extends string = string, + DateTimeFormats extends object = {}, + ResourceKeys extends PickupFormatKeys = PickupFormatKeys + >( + value: Value, + key: Key | ResourceKeys + ): DateTimeFormatResult /** * Datetime formatting * @@ -539,7 +750,16 @@ declare module '@vue/runtime-core' { * * @returns formatted value */ - $d(value: number | Date, key: string, locale: Locale): DateTimeFormatResult + $d< + Value extends number | Date = number, + Key extends string = string, + DateTimeFormats extends object = {}, + ResourceKeys extends PickupFormatKeys = PickupFormatKeys + >( + value: Value, + key: Key | ResourceKeys, + locale: Locale + ): DateTimeFormatResult /** * Datetime formatting * @@ -577,7 +797,15 @@ declare module '@vue/runtime-core' { * * @returns formatted value */ - $d(value: number | Date, key: string): string + $d< + Value extends number | Date = number, + Key extends string = string, + DateTimeFormats extends object = {}, + ResourceKeys extends PickupFormatKeys = PickupFormatKeys + >( + value: Value, + key: Key | ResourceKeys + ): string /** * Datetime formatting * @@ -590,7 +818,16 @@ declare module '@vue/runtime-core' { * * @returns formatted value */ - $d(value: number | Date, key: string, locale: Locale): string + $d< + Value extends number | Date = number, + Key extends string = string, + DateTimeFormats extends object = {}, + ResourceKeys extends PickupFormatKeys = PickupFormatKeys + >( + value: Value, + key: Key | ResourceKeys, + locale: Locale + ): string /** * Datetime formatting * @@ -630,7 +867,14 @@ declare module '@vue/runtime-core' { * * @returns formatted value */ - $n(value: number, key: string): NumberFormatResult + $n< + Key extends string = string, + NumberFormats extends object = {}, + ResourceKeys extends PickupFormatKeys = PickupFormatKeys + >( + value: number, + key: Key | ResourceKeys + ): NumberFormatResult /** * Number formatting * @@ -643,7 +887,15 @@ declare module '@vue/runtime-core' { * * @returns formatted value */ - $n(value: number, key: string, locale: Locale): NumberFormatResult + $n< + Key extends string = string, + NumberFormats extends object = {}, + ResourceKeys extends PickupFormatKeys = PickupFormatKeys + >( + value: number, + key: Key | ResourceKeys, + locale: Locale + ): NumberFormatResult /** * Number formatting * @@ -678,7 +930,14 @@ declare module '@vue/runtime-core' { * * @returns formatted value */ - $n(value: number, key: string): string + $n< + Key extends string = string, + NumberFormats extends object = {}, + ResourceKeys extends PickupFormatKeys = PickupFormatKeys + >( + value: number, + key: Key | ResourceKeys + ): string /** * Number formatting * @@ -691,7 +950,15 @@ declare module '@vue/runtime-core' { * * @returns formatted value */ - $n(value: number, key: string, locale: Locale): string + $n< + Key extends string = string, + NumberFormats extends object = {}, + ResourceKeys extends PickupFormatKeys = PickupFormatKeys + >( + value: number, + key: Key | ResourceKeys, + locale: Locale + ): string /** * Number formatting * @@ -720,6 +987,12 @@ declare module '@vue/runtime-core' { * * @returns locale messages */ - $tm(key: Path): LocaleMessageValue | {} + $tm< + Key extends string, + Messages extends object = {}, + ResourceKeys extends PickupKeys = PickupKeys + >( + key: Key | ResourceKeys + ): LocaleMessageValue | {} } } diff --git a/packages/vue-i18n/test/composer.test.ts b/packages/vue-i18n/test/composer.test.ts index f66bb6211..d7d17e4e3 100644 --- a/packages/vue-i18n/test/composer.test.ts +++ b/packages/vue-i18n/test/composer.test.ts @@ -440,7 +440,7 @@ describe('fallbackRoot', () => { fr: {} }, __root: root - } as any) + }) expect(t('hello')).toEqual('hello') expect(mockWarn).toHaveBeenCalled() expect(mockWarn.mock.calls[0][0]).toEqual( @@ -473,7 +473,7 @@ describe('fallbackRoot', () => { fr: {} }, __root: root - } as any) + }) expect(t('hello')).toEqual('hello') expect(mockWarn).not.toHaveBeenCalledTimes(1) }) @@ -985,8 +985,7 @@ describe('tm', () => { const composer = createComposer({ locale: 'ja', messages: { - en: {}, - ja: { + en: { foo: { bar: { buz: 'hello' @@ -995,14 +994,24 @@ describe('tm', () => { errors: ['error1', 'error2'] } } + }, + ja: { + foo: { + bar: { + buz: 'こんにちは' + }, + codes: { + errors: ['エラー1', 'エラー2'] + } + } } } }) let messages1 = composer.tm('foo.bar') let messages2 = composer.tm('foo.codes') - expect(messages1).toEqual({ buz: 'hello' }) - expect(messages2).toEqual({ errors: ['error1', 'error2'] }) + expect(messages1).toEqual({ buz: 'こんにちは' }) + expect(messages2).toEqual({ errors: ['エラー1', 'エラー2'] }) watchEffect(() => { messages1 = composer.tm('foo.bar') @@ -1060,10 +1069,9 @@ describe('tm', () => { test('resolved with rt', () => { const { rt, tm } = createComposer({ - locale: 'ja', + locale: 'en', messages: { - en: {}, - ja: { + en: { foo: { bar: { buz: 'hello, {name}!' @@ -1072,14 +1080,22 @@ describe('tm', () => { errors: [() => 'error1', () => 'error2'] } } + }, + ja: { + foo: { + bar: { + buz: 'こんにちは、 {name}!' + }, + codes: { + errors: [() => 'エラー1', () => 'エラー2'] + } + } } } }) - expect(rt((tm('foo.bar') as any).buz, { name: 'dio' })).toEqual( - 'hello, dio!' - ) - const errors = tm('foo.codes.errors') as (() => string)[] + expect(rt(tm('foo.bar').buz, { name: 'dio' })).toEqual('hello, dio!') + const errors = tm('foo.codes.errors') for (const [index, err] of errors.entries()) { expect(rt(err)).toEqual(`error${index + 1}`) } @@ -1100,7 +1116,7 @@ test('te', async () => { expect(te('message.hello')).toEqual(true) expect(te('message.hallo')).toEqual(false) - expect(te('message.hallo', 'ja')).toEqual(false) + expect(te('message.hallo', 'ja' as any)).toEqual(false) }) describe('getLocaleMessage / setLocaleMessage / mergeLocaleMessage', () => { @@ -1117,10 +1133,14 @@ describe('getLocaleMessage / setLocaleMessage / mergeLocaleMessage', () => { expect(getLocaleMessage('en')).toEqual({ hello: 'Hello!' }) setLocaleMessage('en', { hi: { hi: 'Hi!' } }) - expect(getLocaleMessage('en')).toEqual({ hi: { hi: 'Hi!' } }) + expect(getLocaleMessage<{ hi: { hi: string } }>('en')).toEqual({ + hi: { hi: 'Hi!' } + }) mergeLocaleMessage('en', { hi: { hello: 'Hello!' } }) - expect(getLocaleMessage('en')).toEqual({ + expect( + getLocaleMessage<{ hi: { hi: string; hello: string } }>('en') + ).toEqual({ hi: { hi: 'Hi!', hello: 'Hello!' @@ -1136,6 +1156,7 @@ describe('getDateTimeFormat / setDateTimeFormat / mergeDateTimeFormat', () => { setDateTimeFormat, mergeDateTimeFormat } = createComposer({ + locale: 'en-US', datetimeFormats: { 'en-US': { short: { @@ -1413,14 +1434,12 @@ describe('__i18n', () => { ja: { foo: msgFnJa } } } - const { messages } = createComposer( - options as ComposerOptions - ) - expect(messages.value!.en).toEqual({ + const { messages } = createComposer(options) + expect(messages.value.en).toEqual({ hello: enI18nFn, foo: msgFnEn }) - expect(messages.value!.ja).toEqual({ + expect(messages.value.ja).toEqual({ hello: jaI18nFn, foo: msgFnJa }) @@ -1453,10 +1472,8 @@ describe('__i18n', () => { } } } - const { messages } = createComposer( - options as ComposerOptions - ) - expect(messages.value!.en).toEqual({ + const { messages } = createComposer(options) + expect(messages.value.en).toEqual({ str: 'str_custom', array1: ['array1_custom'], array2: ['array2_custom'], diff --git a/packages/vue-i18n/test/helper.ts b/packages/vue-i18n/test/helper.ts index 8e37e0dc4..2f9b30fe4 100644 --- a/packages/vue-i18n/test/helper.ts +++ b/packages/vue-i18n/test/helper.ts @@ -76,14 +76,9 @@ afterAll(() => { activeWrapperRemovers = [] }) -export function mount< - Messages = {}, - DateTimeFormats = {}, - NumberFormats = {}, - Legacy extends boolean = true ->( +export function mount( targetComponent: Parameters[0], - i18n: I18n, + i18n: I18n, options: Partial = {} ): Promise { const TargetComponent = targetComponent diff --git a/packages/vue-i18n/test/i18n.test.ts b/packages/vue-i18n/test/i18n.test.ts index 7fcb12429..f51898317 100644 --- a/packages/vue-i18n/test/i18n.test.ts +++ b/packages/vue-i18n/test/i18n.test.ts @@ -54,7 +54,7 @@ describe('createI18n with flat json messages', () => { }) test('composition mode', () => { - const i18n = createI18n({ + const i18n = createI18n({ legacy: false, flatJson: true, messages: { @@ -268,7 +268,7 @@ describe('useI18n', () => { locale: '', resource: { ja: { hello: 'こんにちは、世界!' } } } - ] + ] as any // eslint-disable-line @typescript-eslint/no-explicit-any composer = useI18n() return { t: (composer as Composer).t } }, @@ -451,7 +451,7 @@ describe('slot reactivity', () => { }) test('composable', async () => { - const i18n = createI18n({ + const i18n = createI18n({ legacy: false, locale: 'ja', fallbackLocale: ['en'], @@ -537,7 +537,7 @@ describe('slot reactivity', () => { ` }) - const { html } = await mount(App, i18n) + const { html } = await mount(App, i18n as any) // eslint-disable-line @typescript-eslint/no-explicit-any expect(html()).toMatchSnapshot('ja') i18n.global.locale.value = 'en' await nextTick() diff --git a/packages/vue-i18n/test/legacy.test.ts b/packages/vue-i18n/test/legacy.test.ts index de6a611a4..d72b4c851 100644 --- a/packages/vue-i18n/test/legacy.test.ts +++ b/packages/vue-i18n/test/legacy.test.ts @@ -218,7 +218,7 @@ describe('t', () => { }) expect(() => { - i18n.t(4 as unknown) + i18n.t(4 as any) // eslint-disable-line @typescript-eslint/no-explicit-any }).toThrowError(errorMessages[I18nErrorCodes.INVALID_ARGUMENT]) }) }) @@ -301,7 +301,7 @@ describe('tc', () => { }) expect(() => { - i18n.tc(4 as unknown, 4) + i18n.tc(4 as any, 4) // eslint-disable-line @typescript-eslint/no-explicit-any }).toThrowError(errorMessages[I18nErrorCodes.INVALID_ARGUMENT]) }) }) @@ -314,7 +314,8 @@ test('te', () => { message: { hello: 'Hello!' } - } + }, + ja: {} } }) diff --git a/packages/vue-i18n/test/mixin.test.ts b/packages/vue-i18n/test/mixin.test.ts index a15965006..e9361a120 100644 --- a/packages/vue-i18n/test/mixin.test.ts +++ b/packages/vue-i18n/test/mixin.test.ts @@ -58,7 +58,7 @@ describe('beforeCreate', () => { __i18n: [ { locale: '', - resource: messages + resource: messages as any // eslint-disable-line @typescript-eslint/no-explicit-any } ] }) diff --git a/scripts/api/processor.js b/scripts/api/processor.js index 3c285802d..25ab05cb1 100644 --- a/scripts/api/processor.js +++ b/scripts/api/processor.js @@ -5,6 +5,15 @@ const { createContentBuilder } = require('api-docs-gen') +function escapeTitle(text) { + return text + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/\n/g, '') + .replace(//g, '>') +} + function process(model, pkg, style, resolver, customTags) { // console.log('custom process', model, pkg, style, customTags) @@ -137,6 +146,7 @@ function process(model, pkg, style, resolver, customTags) { function buildFunction(model, builder) { model.summary && buildSummary(model, builder) model.signature && buildSignature(model, builder) + model.typeParameters && buildTypeParameters(model, builder) model.deprecated && buildDeprecated(model, builder) model.remarks && buildDetails(model, builder) model.tips && buildTips(model, builder) @@ -156,6 +166,7 @@ function buildEnum(model, builder) { function buildInterface(model, builder) { model.summary && buildSummary(model, builder) model.signature && buildSignature(model, builder) + model.typeParameters && buildTypeParameters(model, builder) model.deprecated && buildDeprecated(model, builder) model.remarks && buildDetails(model, builder) model.tips && buildTips(model, builder) @@ -172,6 +183,14 @@ function buildInterface(model, builder) { } } + if (model.functions) { + for (const m of model.functions) { + builder.pushline(`### ${m.name}`) + builder.newline() + buildMethodSignature(m, builder) + } + } + if (model.methods) { for (const m of model.methods) { builder.pushline(`### ${m.name}`) @@ -184,6 +203,7 @@ function buildInterface(model, builder) { function buildPropertySignature(model, builder) { model.summary && buildSummary(model, builder) model.signature && buildSignature(model, builder) + model.typeParameters && buildTypeParameters(model, builder, 4) model.deprecated && buildDeprecated(model, builder) model.remarks && buildDetails(model, builder) model.tips && buildTips(model, builder) @@ -197,6 +217,7 @@ function buildPropertySignature(model, builder) { function buildMethodSignature(model, builder) { model.summary && buildSummary(model, builder) model.signature && buildSignature(model, builder) + model.typeParameters && buildTypeParameters(model, builder, 4) model.deprecated && buildDeprecated(model, builder) model.remarks && buildDetails(model, builder) model.seeAlso && buildSeeAlso(model, builder) @@ -216,6 +237,7 @@ function buildClass(model, builder) { function buildTypeAlias(model, builder) { model.summary && buildSummary(model, builder) model.signature && buildSignature(model, builder) + model.typeParameters && buildTypeParameters(model, builder) model.deprecated && buildDeprecated(model, builder) model.remarks && buildDetails(model, builder) model.tips && buildTips(model, builder) @@ -250,6 +272,17 @@ function buildSignature(model, builder) { builder.newline() } +function buildTypeParameters(model, builder, level = 3) { + builder.pushline(`${'#'.repeat(level)} Type Parameters`) + builder.newline() + builder.pushline(`| Parameter | Description |`) + builder.pushline(`| --- | --- |`) + for (const p of model.typeParameters) { + builder.pushline(`| ${p.name} | ${p.description} |`) + } + builder.newline() +} + function buildDeprecated(model, builder) { builder.pushline(`:::danger DEPRECATED`) builder.pushline(model.deprecated) @@ -394,6 +427,17 @@ function parseFunction(style, model, pkg, resolver, item, customTags) { // signature genModel.signature = getSignature(item) + // type parameters + genModel.typeParameters = getTypeParameters( + docs, + style, + model, + pkg, + resolver, + item, + customTags + ) + // deprecated genModel.deprecated = getDeprecated( docs, @@ -629,6 +673,9 @@ function getNameSignature(item, type) { return `${ type === 'constrcutor' ? 'constructor' : item.displayName }(${item.parameters.map(p => p.name).join(', ')})` + } else if (type === 'function') { + const display = item.excerptTokens.map(token => token.text).join('') + return escapeTitle(display.slice(display.indexOf('('))) } else { return item.displayName } @@ -670,6 +717,17 @@ function parseContentForClassinizable( // signature genModel.signature = getSignature(item) + // type parameters + genModel.typeParameters = getTypeParameters( + docs, + style, + model, + pkg, + resolver, + item, + customTags + ) + // deprecated genModel.deprecated = getDeprecated( docs, @@ -742,7 +800,7 @@ function parseContentForClassinizable( ) // parameters - if (type === 'constrcutor' || type === 'method') { + if (type === 'constrcutor' || type === 'method' || type === 'function') { genModel.parameters = getParameters( docs, style, @@ -755,7 +813,7 @@ function parseContentForClassinizable( } // returns - if (type === 'method') { + if (type === 'method' || type === 'function') { genModel.returns = getReturns( docs, style, @@ -768,7 +826,7 @@ function parseContentForClassinizable( } // throws - if (type === 'constructor' || type === 'method') { + if (type === 'constructor' || type === 'method' || type === 'function') { genModel.throws = getThrows( docs, style, @@ -805,6 +863,24 @@ function parseInterface(style, model, pkg, resolver, item, customTags) { customTags ) + const functions = item.members.filter(m => m.kind === 'CallSignature') + if (functions.length > 0) { + genModel.functions = [] + for (const func of functions) { + genModel.functions.push( + parseContentForClassinizable( + style, + model, + pkg, + resolver, + func, + 'function', + customTags + ) + ) + } + } + const methods = item.members.filter(m => m.kind === 'MethodSignature') if (methods.length > 0) { genModel.methods = [] @@ -877,6 +953,17 @@ function parseTypeAlias(style, model, pkg, resolver, item, customTags) { // signature genModel.signature = getSignature(item) + // type parameters + genModel.typeParameters = getTypeParameters( + docs, + style, + model, + pkg, + resolver, + item, + customTags + ) + // deprecated genModel.deprecated = getDeprecated( docs, @@ -1079,6 +1166,37 @@ function getSignature(item) { : undefined } +function getTypeParameters( + docs, + style, + model, + pkg, + resolver, + item, + customTags +) { + if (docs.typeParams && docs.typeParams.count > 0) { + return docs.typeParams.blocks.map(b => { + return { + name: b.parameterName, + description: b.content + ? getDocSectionContent( + model, + pkg, + b.content, + item, + style, + resolver, + customTags + ) + : '' + } + }) + } else { + return undefined + } +} + function getDeprecated(docs, style, model, pkg, resolver, item, customTags) { if (docs.deprecatedBlock) { return getDocSectionContent( diff --git a/test-d/core-base/context.test-d.ts b/test-d/core-base/context.test-d.ts new file mode 100644 index 000000000..e1dc7ed47 --- /dev/null +++ b/test-d/core-base/context.test-d.ts @@ -0,0 +1,194 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { expectType } from '../index' + +import { + CoreOptions, + SchemaParams, + LocaleParams, + PickupFallbackLocales, + createCoreContext, + Locale, + FallbackLocale +} from '../../packages/core-base/src' +import type { + ResourceSchema, + MyDatetimeScehma, + MyNumberSchema +} from '../schema' + +// loose options +const looseOptions = { + locale: 'en', + fallbackLocale: { + ja: ['en'] + }, + messages: { + en: { + foo: 'foo', + nest: { + bar: 'bar' + } + }, + ja: { + bar: 'foo', + nest: { + bar: 'bar' + } + } + }, + datetimeFormats: { + 'en-US': { + short: {} + } + }, + numberFormats: { + 'ja-JP': { + currency: {} + } + } +} + +// strict options +const strictOptions = { + locale: 'en', + fallbackLocale: { + ja: ['en'] + }, + messages: { + en: { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error1'] + }, + ja: { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error2'] + } + } +} + +expectType(looseOptions) +expectType< + CoreOptions< + string, + SchemaParams< + { + message: ResourceSchema + datetime: MyDatetimeScehma + number: MyNumberSchema + }, + string + >, + LocaleParams<'en' | 'ja'> + > +>(strictOptions) + +// check loose context +const looseCtx = createCoreContext(looseOptions) +expectType<'en' | 'ja' | 'en-US' | 'ja-JP'>(looseCtx.locale) +expectType< + | 'en' + | 'ja' + | 'en-US' + | 'ja-JP' + | ('en' | 'ja' | 'en-US' | 'ja-JP')[] + | { + [x in string]: PickupFallbackLocales<['en' | 'ja' | 'en-US' | 'ja-JP']>[] + } + | false +>(looseCtx.fallbackLocale) +expectType<{ + en: { + foo: string + nest: { + bar: string + } + } + ja: { + bar: string + nest: { + bar: string + } + } +}>(looseCtx.messages) +expectType<{ 'en-US': { short: {} } }>(looseCtx.datetimeFormats) +expectType<{ 'ja-JP': { currency: {} } }>(looseCtx.numberFormats) + +// check strict context +const strictCtx = createCoreContext<[ResourceSchema], 'en' | 'ja'>( + strictOptions +) +expectType<'en' | 'ja'>(strictCtx.locale) +expectType< + | 'en' + | 'ja' + | ('en' | 'ja')[] + | { [x in string]: PickupFallbackLocales<['en' | 'ja']>[] } + | false +>(strictCtx.fallbackLocale) +expectType<{ en: ResourceSchema; ja: ResourceSchema }>(strictCtx.messages) +expectType<{ en: {}; ja: {} }>(strictCtx.datetimeFormats) +expectType<{ en: {}; ja: {} }>(strictCtx.numberFormats) + +// check strict context with direct options +const strictDirectCtx = createCoreContext< + { + message: ResourceSchema + datetime: MyDatetimeScehma + number: MyNumberSchema + }, + { messages: 'en'; datetimeFormats: 'ja-JP' | 'zh'; numberFormats: 'ca' } +>({ + messages: { + en: { + foo: '', + nest: { + bar: '' + }, + errors: [''] + } + }, + datetimeFormats: { + zh: { + short: { + hour: 'numeric' + } + }, + 'ja-JP': { + short: { + hour: 'numeric' + } + } + }, + numberFormats: { + ca: { + currency: { style: 'symbol' } + } + } +}) +expectType<'en' | 'zh' | 'ca' | 'ja-JP'>(strictDirectCtx.locale) +expectType< + | 'en' + | 'zh' + | 'ca' + | 'ja-JP' + | ('en' | 'zh' | 'ca' | 'ja-JP')[] + | { [x in string]: PickupFallbackLocales<['en' | 'zh' | 'ca' | 'ja-JP']>[] } + | false +>(strictDirectCtx.fallbackLocale) +expectType<{ en: ResourceSchema }>(strictDirectCtx.messages) +expectType<{ zh: {}; 'ja-JP': { short: {} } }>(strictDirectCtx.datetimeFormats) +expectType<{ ca: { currency: {} } }>(strictDirectCtx.numberFormats) + +const nullCtx = createCoreContext({}) +expectType(nullCtx.locale) +nullCtx.locale = 'ja' +expectType(nullCtx.fallbackLocale) +nullCtx.fallbackLocale = 'en' + +/* eslint-enable @typescript-eslint/no-explicit-any */ diff --git a/test-d/core-base/datetime.test-d.ts b/test-d/core-base/datetime.test-d.ts new file mode 100644 index 000000000..550ff700a --- /dev/null +++ b/test-d/core-base/datetime.test-d.ts @@ -0,0 +1,39 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { expectType } from '../index' + +import { createCoreContext } from '../../packages/core-base/src' +import { datetime } from '../../packages/core-base/src' + +const ctx = createCoreContext({ + locale: 'en-US', + datetimeFormats: { + 'en-US': { + short: { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + timeZone: 'America/New_York' + } + } + } +}) + +const dt = new Date(Date.UTC(2012, 11, 20, 3, 0, 0)) + +expectType(datetime(ctx, dt)) +expectType( + datetime(ctx, dt, { locale: 'en-US', key: 'short' }) +) +expectType( + datetime(ctx, dt, { key: 'short' }, 'en-US') +) +expectType( + datetime(ctx, dt, { key: 'short' }, { hourCycle: 'h24' }) +) +expectType( + datetime(ctx, dt, { key: 'short' }, 'en-US', { hourCycle: 'h24' }) +) + +/* eslint-enable @typescript-eslint/no-explicit-any */ diff --git a/test-d/core-base/number.test-d.ts b/test-d/core-base/number.test-d.ts new file mode 100644 index 000000000..1532d6c04 --- /dev/null +++ b/test-d/core-base/number.test-d.ts @@ -0,0 +1,34 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { expectType } from '../index' + +import { createCoreContext } from '../../packages/core-base/src' +import { number } from '../../packages/core-base/src' + +const ctx = createCoreContext({ + locale: 'en-US', + numberFormats: { + 'en-US': { + currency: { + style: 'currency', + currency: 'USD', + currencyDisplay: 'symbol' + } + } + } +}) + +expectType(number(ctx, 10100)) +expectType( + number(ctx, 10100, { locale: 'en-US', key: 'currency' }) +) +expectType( + number(ctx, 10100, { key: 'currency' }, 'en-US') +) +expectType( + number(ctx, 10100, { locale: 'en-US', key: 'currency' }, { unit: '' }) +) +expectType( + number(ctx, 10100, { key: 'currency' }, 'en-US', { unit: '' }) +) + +/* eslint-enable @typescript-eslint/no-explicit-any */ diff --git a/test-d/core-base/translate.test-d.ts b/test-d/core-base/translate.test-d.ts new file mode 100644 index 000000000..e9ea92a08 --- /dev/null +++ b/test-d/core-base/translate.test-d.ts @@ -0,0 +1,43 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { expectType } from '../index' + +import { createCoreContext } from '../../packages/core-base/src' +import { translate } from '../../packages/core-base/src' + +const ctx = createCoreContext({ + locale: 'en', + messages: { + en: { + hello: 'hello world!' + } + } +}) + +expectType(translate(ctx, 'hello')) +expectType(translate(ctx, 'hello', 1)) +expectType( + translate(ctx, 'hello', 1, { locale: 'en', missingWarn: true }) +) +expectType(translate(ctx, 'hello', 'default msg')) +expectType( + translate(ctx, 'hello', 'default msg', { locale: 'en', plural: 2 }) +) +expectType(translate(ctx, 'hello', ['list'])) +expectType(translate(ctx, 'hello', ['list'], 1)) +expectType(translate(ctx, 'hello', ['list'], 'default msg')) +expectType(translate(ctx, 'hello', ['list'], { locale: 'en' })) +expectType(translate(ctx, 'hello', { name: 'dio' })) +expectType(translate(ctx, 'hello', { name: 'dio' }, 1)) +expectType( + translate(ctx, 'hello', { name: 'dio' }, 'default msg') +) +expectType( + translate( + ctx, + 'hello', + { name: 'dio' }, + { locale: 'en', resolvedMessage: true } + ) +) + +/* eslint-enable @typescript-eslint/no-explicit-any */ diff --git a/test-d/schema.ts b/test-d/schema.ts new file mode 100644 index 000000000..7a1d83cd8 --- /dev/null +++ b/test-d/schema.ts @@ -0,0 +1,19 @@ +export type ResourceSchema = { + foo: string + nest: { + bar: string + } + errors: string[] +} + +export type MyDatetimeScehma = { + short: { + hour: 'numeric' + } +} + +export type MyNumberSchema = { + currency: { + style: 'symbol' + } +} diff --git a/test-d/vue-i18n/composer.test-d.ts b/test-d/vue-i18n/composer.test-d.ts new file mode 100644 index 000000000..d23579654 --- /dev/null +++ b/test-d/vue-i18n/composer.test-d.ts @@ -0,0 +1,321 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { expectType } from '..' + +import { + Locale, + FallbackLocale, + LocaleMessageValue, + PickupFallbackLocales +} from '../../packages/core-base/src' +import { + ComposerOptions, + createComposer +} from '../../packages/vue-i18n/src/composer' +import { SchemaParams, LocaleParams } from '../../packages/core-base/src' +import { ResourceSchema, MyDatetimeScehma, MyNumberSchema } from '../schema' + +// loose options +const looseOptions = { + locale: 'en', + fallbackLocale: { + ja: ['en'] + }, + messages: { + en: { + foo: 'foo', + nest: { + bar: 'bar' + } + }, + ja: { + bar: 'foo', + nest: { + bar: 'bar' + } + } + }, + datetimeFormats: { + 'en-US': { + short: {} + } + }, + numberFormats: { + 'ja-JP': { + currency: {} + } + } +} + +// strict options +const strictOptions = { + locale: 'en', + fallbackLocale: { + ja: ['en'] + }, + messages: { + en: { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error1'] + }, + ja: { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error2'] + } + } +} + +expectType(looseOptions) +expectType< + ComposerOptions< + string, + SchemaParams< + { + message: ResourceSchema + datetime: MyDatetimeScehma + number: MyNumberSchema + }, + string + >, + LocaleParams<'en' | 'ja'> + > +>(strictOptions) + +// check loose composer +const looseComposer = createComposer(looseOptions) +expectType<'en' | 'ja' | 'en-US' | 'ja-JP'>(looseComposer.locale.value) +expectType< + | 'en' + | 'ja' + | 'en-US' + | 'ja-JP' + | ('en' | 'ja' | 'en-US' | 'ja-JP')[] + | { + [x in string]: PickupFallbackLocales<['en' | 'ja' | 'en-US' | 'ja-JP']>[] + } + | false +>(looseComposer.fallbackLocale.value) +expectType<{ + en: { + foo: string + nest: { + bar: string + } + } + ja: { + bar: string + nest: { + bar: string + } + } +}>(looseComposer.messages.value) +expectType<{ 'en-US': { short: {} } }>(looseComposer.datetimeFormats.value) +expectType<{ 'ja-JP': { currency: {} } }>(looseComposer.numberFormats.value) +expectType(looseComposer.t('nest.bar')) +expectType(looseComposer.t('nest', 1, { locale: 'en' })) +expectType(looseComposer.t('foo', 'default msg', { locale: 'en' })) +expectType(looseComposer.t('errors', [1], { plural: 1 })) +expectType(looseComposer.t('errors', [1], 1)) +expectType(looseComposer.t('errors', [1], 'default msg')) +expectType(looseComposer.t(1, { foo: 1 }, { locale: 'en' })) +expectType(looseComposer.t('nestt', { foo: 1 }, 'msg')) +expectType(looseComposer.te('errors', 'en')) +expectType<{ bar: string }>(looseComposer.tm('nest')) +expectType(looseComposer.tm('errors')) +expectType(looseComposer.rt('foo')) +expectType( + looseComposer.getLocaleMessage('en') +) +expectType<{ japan: string }>( + looseComposer.getLocaleMessage<{ japan: string }>('japan') +) +looseComposer.setLocaleMessage('en', { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error1'] +}) +looseComposer.setLocaleMessage<{ dio: string }>('jojo', { + dio: 'The world!' +}) +looseComposer.mergeLocaleMessage('en', { + bar: 'foo' +}) +looseComposer.mergeLocaleMessage<{ dio: string }>('en', { + dio: 'The world!' +}) +expectType( + looseComposer.getDateTimeFormat('en-US') +) +expectType<{ long: { hour: string } }>( + looseComposer.getLocaleMessage<{ long: { hour: string } }>('en-US') +) +looseComposer.setDateTimeFormat('en-US', { + long: { + hour: 'numeric' + } +}) +looseComposer.setDateTimeFormat<{ stop: { hour: string } }>('world', { + stop: { hour: 'infinity' } +}) +looseComposer.mergeDateTimeFormat('en-US', { + long: { hour: 'numeric' } +}) +looseComposer.mergeDateTimeFormat<{ stop: { hour: string } }>('en-US', { + stop: { hour: 'infinity' } +}) +expectType( + looseComposer.getNumberFormat('ja-JP') +) +expectType<{ weight: { unit: string } }>( + looseComposer.getNumberFormat<{ weight: { unit: string } }>('en-US') +) +looseComposer.setNumberFormat('en-US', { + weight: { + unit: 'kiro' + } +}) +looseComposer.setNumberFormat<{ echoes: { act: string } }>('stand', { + echoes: { act: '2' } +}) +looseComposer.mergeNumberFormat('ja-JP', { + weight: { + unit: 'kiro' + } +}) +looseComposer.mergeNumberFormat<{ echoes: { act: string } }>('ja-JP', { + echoes: { act: '2' } +}) + +// check strict composer +const strictComposer = createComposer<[ResourceSchema], 'en' | 'ja'>( + strictOptions +) +expectType<'en' | 'ja'>(strictComposer.locale.value) +expectType< + | 'en' + | 'ja' + | ('en' | 'ja')[] + | { [x in string]: PickupFallbackLocales<['en' | 'ja']>[] } + | false +>(strictComposer.fallbackLocale.value) +expectType<{ en: ResourceSchema; ja: ResourceSchema }>( + strictComposer.messages.value +) +expectType<{ en: {}; ja: {} }>(strictComposer.datetimeFormats.value) +expectType<{ en: {}; ja: {} }>(strictComposer.numberFormats.value) +expectType(strictComposer.t('nest.bar')) +expectType(strictComposer.t('nest', 1, { locale: 'en' })) +expectType(strictComposer.t('foo', 'default msg', { locale: 'en' })) +expectType(strictComposer.t('errors', [1], { plural: 1 })) +expectType(strictComposer.t('errors', [1], 1)) +expectType(strictComposer.t('errors', [1], 'default msg')) +expectType(strictComposer.t(1, { foo: 1 }, { locale: 'en' })) +expectType(strictComposer.t('nestt', { foo: 1 }, 'msg')) +expectType(strictComposer.te('errors', 'en')) +expectType<{ bar: string }>(strictComposer.tm('nest')) +expectType(strictComposer.tm('errors')) +expectType(strictComposer.rt('foo')) +expectType( + strictComposer.getLocaleMessage('en') +) +expectType<{ japan: string }>( + strictComposer.getLocaleMessage<{ japan: string }>('japan') +) +strictComposer.setLocaleMessage('en', { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error1'] +}) +strictComposer.setLocaleMessage<{ dio: string }>('jojo', { + dio: 'The world!' +}) +strictComposer.mergeLocaleMessage('en', { + bar: 'foo' +}) +strictComposer.mergeLocaleMessage<{ dio: string }>('en', { + dio: 'The world!' +}) + +// check strict context with direct options +const strictDirectComposer = createComposer< + { + message: ResourceSchema + datetime: MyDatetimeScehma + number: MyNumberSchema + }, + { messages: 'en'; datetimeFormats: 'ja-JP' | 'zh'; numberFormats: 'ca' } +>({ + messages: { + en: { + foo: '', + nest: { + bar: '' + }, + errors: [''] + } + }, + datetimeFormats: { + zh: { + short: { + hour: 'numeric' + } + }, + 'ja-JP': { + short: { + hour: 'numeric' + } + } + }, + numberFormats: { + ca: { + currency: { style: 'symbol' } + } + } +}) +expectType<'en' | 'zh' | 'ca' | 'ja-JP'>(strictDirectComposer.locale.value) +expectType< + | 'en' + | 'zh' + | 'ca' + | 'ja-JP' + | ('en' | 'zh' | 'ca' | 'ja-JP')[] + | { [x in string]: PickupFallbackLocales<['en' | 'zh' | 'ca' | 'ja-JP']>[] } + | false +>(strictDirectComposer.fallbackLocale.value) +expectType<{ en: ResourceSchema }>(strictDirectComposer.messages.value) +expectType<{ zh: {}; 'ja-JP': { short: {} } }>( + strictDirectComposer.datetimeFormats.value +) +expectType<{ ca: { currency: {} } }>(strictDirectComposer.numberFormats.value) +expectType(strictDirectComposer.d(new Date())) +expectType(strictDirectComposer.d(new Date(), 'short', 'ja-JP')) +expectType( + strictDirectComposer.d(new Date(), { key: 'short', locale: 'zh' }) +) +expectType(strictDirectComposer.d(new Date(), 'custom' as any)) +expectType(strictDirectComposer.n(1)) +expectType(strictDirectComposer.n(1, 'currency', 'zh')) +expectType(strictDirectComposer.n(1, { key: 'currency', locale: 'en' })) +expectType(strictDirectComposer.n(1, 'custom' as any)) + +// const noOptionsComposer = createComposer({ missingWarn: true }) +const noOptionsComposer = createComposer({ locale: 'en' }) +expectType(noOptionsComposer.locale.value) +expectType(noOptionsComposer.fallbackLocale.value) + +const nullComposer = createComposer({}) +expectType(nullComposer.locale.value) +nullComposer.locale.value = 'en' +expectType(nullComposer.fallbackLocale.value) +nullComposer.fallbackLocale.value = 'fr' + +/* eslint-enable @typescript-eslint/no-explicit-any */ diff --git a/test-d/vue-i18n/i18n.test-d.ts b/test-d/vue-i18n/i18n.test-d.ts new file mode 100644 index 000000000..412540dca --- /dev/null +++ b/test-d/vue-i18n/i18n.test-d.ts @@ -0,0 +1,428 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { expectType } from '..' + +import { + LocaleMessageValue, + PickupFallbackLocales +} from '../../packages/core-base/src' +import { + UseI18nOptions, + I18nOptions, + useI18n, + createI18n +} from '../../packages/vue-i18n/src/i18n' +import { SchemaParams, LocaleParams } from '../../packages/core-base/src' +import { ResourceSchema, MyDatetimeScehma, MyNumberSchema } from '../schema' + +// loose options +const looseOptions = { + locale: 'en', + fallbackLocale: { + ja: ['en'] + }, + messages: { + en: { + foo: 'foo', + nest: { + bar: 'bar' + } + }, + ja: { + bar: 'foo', + nest: { + bar: 'bar' + } + } + }, + datetimeFormats: { + 'en-US': { + short: {} + } + }, + numberFormats: { + 'ja-JP': { + currency: {} + } + } +} + +// strict options +const strictOptions = { + locale: 'en', + legacy: false, + fallbackLocale: { + ja: ['en'] + }, + messages: { + en: { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error1'] + }, + ja: { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error2'] + } + } +} + +expectType(looseOptions) +expectType< + UseI18nOptions< + SchemaParams< + { + message: ResourceSchema + datetime: MyDatetimeScehma + number: MyNumberSchema + }, + string + >, + LocaleParams<'en' | 'ja'> + > +>(strictOptions) + +// check loose composer +const looseComposer = useI18n(looseOptions) +expectType<'en' | 'ja' | 'en-US' | 'ja-JP'>(looseComposer.locale.value) +expectType< + | 'en' + | 'ja' + | 'en-US' + | 'ja-JP' + | ('en' | 'ja' | 'en-US' | 'ja-JP')[] + | { + [x in string]: PickupFallbackLocales<['en' | 'ja' | 'en-US' | 'ja-JP']>[] + } + | false +>(looseComposer.fallbackLocale.value) +expectType<{ + en: { + foo: string + nest: { + bar: string + } + } + ja: { + bar: string + nest: { + bar: string + } + } +}>(looseComposer.messages.value) +expectType<{ 'en-US': { short: {} } }>(looseComposer.datetimeFormats.value) +expectType<{ 'ja-JP': { currency: {} } }>(looseComposer.numberFormats.value) +expectType(looseComposer.t('nest.bar')) +expectType(looseComposer.t('nest', 1, { locale: 'en' })) +expectType(looseComposer.t('foo', 'default msg', { locale: 'en' })) +expectType(looseComposer.t('errors', [1], { plural: 1 })) +expectType(looseComposer.t('errors', [1], 1)) +expectType(looseComposer.t('errors', [1], 'default msg')) +expectType(looseComposer.t(1, { foo: 1 }, { locale: 'en' })) +expectType(looseComposer.t('nestt', { foo: 1 }, 'msg')) +expectType(looseComposer.te('errors', 'en')) +expectType<{ bar: string }>(looseComposer.tm('nest')) +expectType(looseComposer.tm('errors')) +expectType(looseComposer.rt('foo')) +expectType( + looseComposer.getLocaleMessage('en') +) +expectType<{ japan: string }>( + looseComposer.getLocaleMessage<{ japan: string }>('japan') +) +looseComposer.setLocaleMessage('en', { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error1'] +}) +looseComposer.setLocaleMessage<{ dio: string }>('jojo', { + dio: 'The world!' +}) +looseComposer.mergeLocaleMessage('en', { + bar: 'foo' +}) +looseComposer.mergeLocaleMessage<{ dio: string }>('en', { + dio: 'The world!' +}) +expectType( + looseComposer.getDateTimeFormat('en-US') +) +expectType<{ long: { hour: string } }>( + looseComposer.getLocaleMessage<{ long: { hour: string } }>('en-US') +) +looseComposer.setDateTimeFormat('en-US', { + long: { + hour: 'numeric' + } +}) +looseComposer.setDateTimeFormat<{ stop: { hour: string } }>('world', { + stop: { hour: 'infinity' } +}) +looseComposer.mergeDateTimeFormat('en-US', { + long: { hour: 'numeric' } +}) +looseComposer.mergeDateTimeFormat<{ stop: { hour: string } }>('en-US', { + stop: { hour: 'infinity' } +}) +expectType( + looseComposer.getNumberFormat('ja-JP') +) +expectType<{ weight: { unit: string } }>( + looseComposer.getNumberFormat<{ weight: { unit: string } }>('en-US') +) +looseComposer.setNumberFormat('en-US', { + weight: { + unit: 'kiro' + } +}) +looseComposer.setNumberFormat<{ echoes: { act: string } }>('stand', { + echoes: { act: '2' } +}) +looseComposer.mergeNumberFormat('ja-JP', { + weight: { + unit: 'kiro' + } +}) +looseComposer.mergeNumberFormat<{ echoes: { act: string } }>('ja-JP', { + echoes: { act: '2' } +}) + +// check strict composer +const strictComposer = useI18n<[ResourceSchema], 'en' | 'ja'>(strictOptions) +expectType<'en' | 'ja'>(strictComposer.locale.value) +expectType< + | 'en' + | 'ja' + | ('en' | 'ja')[] + | { [x in string]: PickupFallbackLocales<['en' | 'ja']>[] } + | false +>(strictComposer.fallbackLocale.value) +expectType<{ en: ResourceSchema; ja: ResourceSchema }>( + strictComposer.messages.value +) +expectType<{ en: {}; ja: {} }>(strictComposer.datetimeFormats.value) +expectType<{ en: {}; ja: {} }>(strictComposer.numberFormats.value) +expectType(strictComposer.t('nest.bar')) +expectType(strictComposer.t('nest', 1, { locale: 'en' })) +expectType(strictComposer.t('foo', 'default msg', { locale: 'en' })) +expectType(strictComposer.t('errors', [1], { plural: 1 })) +expectType(strictComposer.t('errors', [1], 1)) +expectType(strictComposer.t('errors', [1], 'default msg')) +expectType(strictComposer.t(1, { foo: 1 }, { locale: 'en' })) +expectType(strictComposer.t('nestt', { foo: 1 }, 'msg')) +expectType(strictComposer.te('errors', 'en')) +expectType<{ bar: string }>(strictComposer.tm('nest')) +expectType(strictComposer.tm('errors')) +expectType(strictComposer.rt('foo')) +expectType( + strictComposer.getLocaleMessage('en') +) +expectType<{ japan: string }>( + strictComposer.getLocaleMessage<{ japan: string }>('japan') +) +strictComposer.setLocaleMessage('en', { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error1'] +}) +strictComposer.setLocaleMessage<{ dio: string }>('jojo', { + dio: 'The world!' +}) +strictComposer.mergeLocaleMessage('en', { + bar: 'foo' +}) +strictComposer.mergeLocaleMessage<{ dio: string }>('en', { + dio: 'The world!' +}) + +// not local scope +const globalComposer = useI18n<[ResourceSchema], 'en' | 'ja'>({ + inheritLocale: true, + useScope: 'global' +}) +expect(globalComposer.t('nest.bar', { foo: 1 }, { locale: 'ja' })) + +expectType(looseOptions) +expectType< + I18nOptions< + SchemaParams< + { + message: ResourceSchema + datetime: MyDatetimeScehma + number: MyNumberSchema + }, + string + >, + LocaleParams<'en' | 'ja'> + > +>(strictOptions) + +// check loose i18n +const looseI18n = createI18n(looseOptions).global +expectType<'en' | 'ja' | 'en-US' | 'ja-JP'>(looseI18n.locale) +expectType< + | 'en' + | 'ja' + | 'en-US' + | 'ja-JP' + | ('en' | 'ja' | 'en-US' | 'ja-JP')[] + | { + [x in string]: PickupFallbackLocales<['en' | 'ja' | 'en-US' | 'ja-JP']>[] + } + | false +>(looseI18n.fallbackLocale) +expectType<{ + en: { + foo: string + nest: { + bar: string + } + } + ja: { + bar: string + nest: { + bar: string + } + } +}>(looseI18n.messages) +expectType<{ 'en-US': { short: {} } }>(looseI18n.datetimeFormats) +expectType<{ 'ja-JP': { currency: {} } }>(looseI18n.numberFormats) +expectType(looseI18n.t('nest.bar')) +expectType(looseI18n.t('nest', 'en')) +expectType(looseI18n.t('nest', 'en', [1])) +expectType(looseI18n.t('nest', 'en', { foo: 'test' })) +expectType(looseI18n.t('foo', [1])) +expectType(looseI18n.t('nest', { foo: 1 })) +expectType(looseI18n.tc('nest')) +expectType(looseI18n.tc('bar', 'en')) +expectType(looseI18n.tc('bar', ['foo'])) +expectType(looseI18n.tc('bar', { foo: 'foo' })) +expectType(looseI18n.tc('nest.bar', 1)) +expectType(looseI18n.tc('nest.bar', 1, ['bar'])) +expectType(looseI18n.tc('nest.bar', 1, { foo: 'bar' })) +expectType(looseI18n.te('errors', 'en')) +expectType<{ bar: string }>(looseI18n.tm('nest')) +expectType(looseI18n.tm('errors')) +expectType(looseI18n.rt('foo')) +expectType(looseI18n.getLocaleMessage('en')) +expectType<{ japan: string }>( + looseI18n.getLocaleMessage<{ japan: string }>('japan') +) +looseI18n.setLocaleMessage('en', { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error1'] +}) +looseI18n.setLocaleMessage<{ dio: string }>('jojo', { + dio: 'The world!' +}) +looseI18n.mergeLocaleMessage('en', { + bar: 'foo' +}) +looseI18n.mergeLocaleMessage<{ dio: string }>('en', { + dio: 'The world!' +}) +expectType( + looseI18n.getDateTimeFormat('en-US') +) +expectType<{ long: { hour: string } }>( + looseI18n.getLocaleMessage<{ long: { hour: string } }>('en-US') +) +looseI18n.setDateTimeFormat('en-US', { + long: { + hour: 'numeric' + } +}) +looseI18n.setDateTimeFormat<{ stop: { hour: string } }>('world', { + stop: { hour: 'infinity' } +}) +looseI18n.mergeDateTimeFormat('en-US', { + long: { hour: 'numeric' } +}) +looseI18n.mergeDateTimeFormat<{ stop: { hour: string } }>('en-US', { + stop: { hour: 'infinity' } +}) +expectType( + looseI18n.getNumberFormat('ja-JP') +) +expectType<{ weight: { unit: string } }>( + looseI18n.getNumberFormat<{ weight: { unit: string } }>('en-US') +) +looseI18n.setNumberFormat('en-US', { + weight: { + unit: 'kiro' + } +}) +looseI18n.setNumberFormat<{ echoes: { act: string } }>('stand', { + echoes: { act: '2' } +}) +looseI18n.mergeNumberFormat('ja-JP', { + weight: { + unit: 'kiro' + } +}) +looseI18n.mergeNumberFormat<{ echoes: { act: string } }>('ja-JP', { + echoes: { act: '2' } +}) + +// check strict i18n +const strictI18n = createI18n<[ResourceSchema], 'en' | 'ja', false>( + strictOptions +).global +expectType<'en' | 'ja'>(strictI18n.locale.value) +expectType< + | 'en' + | 'ja' + | ('en' | 'ja')[] + | { [x in string]: PickupFallbackLocales<['en' | 'ja']>[] } + | false +>(strictI18n.fallbackLocale.value) +expectType<{ en: ResourceSchema; ja: ResourceSchema }>( + strictI18n.messages.value +) +expectType<{ en: {}; ja: {} }>(strictI18n.datetimeFormats.value) +expectType<{ en: {}; ja: {} }>(strictI18n.numberFormats.value) +expectType(strictI18n.t('nest.bar')) +expectType(strictI18n.t('nest', 1, { locale: 'en' })) +expectType(strictI18n.t('foo', 'default msg', { locale: 'en' })) +expectType(strictI18n.t('errors', [1], { plural: 1 })) +expectType(strictI18n.t('errors', [1], 1)) +expectType(strictI18n.t('errors', [1], 'default msg')) +expectType(strictI18n.t(1, { foo: 1 }, { locale: 'en' })) +expectType(strictI18n.t('nestt', { foo: 1 }, 'msg')) +expectType(strictI18n.te('errors', 'en')) +expectType<{ bar: string }>(strictI18n.tm('nest')) +expectType(strictI18n.tm('errors')) +expectType(strictI18n.rt('foo')) +expectType( + strictI18n.getLocaleMessage('en') +) +expectType<{ japan: string }>( + strictI18n.getLocaleMessage<{ japan: string }>('japan') +) +strictI18n.setLocaleMessage('en', { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error1'] +}) +strictI18n.setLocaleMessage<{ dio: string }>('jojo', { + dio: 'The world!' +}) +strictI18n.mergeLocaleMessage('en', { + bar: 'foo' +}) +strictI18n.mergeLocaleMessage<{ dio: string }>('en', { + dio: 'The world!' +}) diff --git a/test-d/vue-i18n/legacy.test-d.ts b/test-d/vue-i18n/legacy.test-d.ts new file mode 100644 index 000000000..0538e15b4 --- /dev/null +++ b/test-d/vue-i18n/legacy.test-d.ts @@ -0,0 +1,249 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { expectType } from '..' + +import { + Locale, + FallbackLocale, + LocaleMessageValue, + PickupFallbackLocales +} from '../../packages/core-base/src' +import { + VueI18nOptions, + createVueI18n +} from '../../packages/vue-i18n/src/legacy' +import { SchemaParams, LocaleParams } from '../../packages/core-base/src' +import { ResourceSchema, MyDatetimeScehma, MyNumberSchema } from '../schema' + +// loose options +const looseOptions = { + locale: 'en', + fallbackLocale: { + ja: ['en'] + }, + messages: { + en: { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error1'] + }, + ja: { + bar: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error2'] + } + }, + datetimeFormats: { + 'en-US': { + short: {} + } + }, + numberFormats: { + 'ja-JP': { + currency: {} + } + } +} + +// strict options +const strictOptions = { + locale: 'en', + fallbackLocale: { + ja: ['en'] + }, + messages: { + en: { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error1'] + }, + ja: { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error2'] + } + } +} + +expectType(looseOptions) +expectType< + VueI18nOptions< + string, + SchemaParams< + { + message: ResourceSchema + datetime: MyDatetimeScehma + number: MyNumberSchema + }, + string + >, + LocaleParams<'en' | 'ja'> + > +>(strictOptions) + +// check loose VueI18n +const looseVueI18n = createVueI18n(looseOptions) +expectType<'en' | 'ja' | 'en-US' | 'ja-JP'>(looseVueI18n.locale) +expectType< + | 'en' + | 'ja' + | 'en-US' + | 'ja-JP' + | ('en' | 'ja' | 'en-US' | 'ja-JP')[] + | { + [x in string]: PickupFallbackLocales<['en' | 'ja' | 'en-US' | 'ja-JP']>[] + } + | false +>(looseVueI18n.fallbackLocale) +expectType<{ + en: { + foo: string + nest: { + bar: string + } + } + ja: { + bar: string + nest: { + bar: string + } + } +}>(looseVueI18n.messages) +expectType<{ 'en-US': { short: {} } }>(looseVueI18n.datetimeFormats) +expectType<{ 'ja-JP': { currency: {} } }>(looseVueI18n.numberFormats) +expectType(looseVueI18n.t('nest.bar')) +expectType(looseVueI18n.t('nest', 'en')) +expectType(looseVueI18n.t('nest', 'en', [1])) +expectType(looseVueI18n.t('nest', 'en', { foo: 'test' })) +expectType(looseVueI18n.t('foo', [1])) +expectType(looseVueI18n.t('nest', { foo: 1 })) +expectType(looseVueI18n.tc('nest')) +expectType(looseVueI18n.tc('bar', 'en')) +expectType(looseVueI18n.tc('bar', ['foo'])) +expectType(looseVueI18n.tc('bar', { foo: 'foo' })) +expectType(looseVueI18n.tc('nest.bar', 1)) +expectType(looseVueI18n.tc('nest.bar', 1, ['bar'])) +expectType(looseVueI18n.tc('nest.bar', 1, { foo: 'bar' })) +expectType(looseVueI18n.te('errors', 'en')) +expectType<{ bar: string }>(looseVueI18n.tm('nest')) +expectType>(looseVueI18n.tm('errors')) +expectType(looseVueI18n.rt('foo')) +expectType(looseVueI18n.getLocaleMessage('en')) +expectType<{ japan: string }>( + looseVueI18n.getLocaleMessage<{ japan: string }>('japan') +) +looseVueI18n.setLocaleMessage('en', { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error1'] +}) +looseVueI18n.setLocaleMessage<{ dio: string }>('jojo', { dio: 'dio' }) +looseVueI18n.mergeLocaleMessage('en', { + bar: 'foo' +}) +looseVueI18n.setDateTimeFormat('en-US', { + long: { + hour: 'numeric' + } +}) +looseVueI18n.setDateTimeFormat<{ stop: { hour: string } }>('world', { + stop: { hour: 'infinity' } +}) +looseVueI18n.mergeDateTimeFormat('en-US', { + long: { hour: 'numeric' } +}) +looseVueI18n.mergeDateTimeFormat<{ stop: { hour: string } }>('en-US', { + stop: { hour: 'infinity' } +}) +expectType( + looseVueI18n.getNumberFormat('ja-JP') +) +expectType<{ weight: { unit: string } }>( + looseVueI18n.getNumberFormat<{ weight: { unit: string } }>('en-US') +) +looseVueI18n.setNumberFormat('en-US', { + weight: { + unit: 'kiro' + } +}) +looseVueI18n.setNumberFormat<{ echoes: { act: string } }>('stand', { + echoes: { act: '2' } +}) +looseVueI18n.mergeNumberFormat('ja-JP', { + weight: { + unit: 'kiro' + } +}) +looseVueI18n.mergeNumberFormat<{ echoes: { act: string } }>('ja-JP', { + echoes: { act: '2' } +}) + +// check strict VueI18n +const strictVueI18n = createVueI18n<[ResourceSchema], 'en' | 'ja'>( + strictOptions +) +expectType<'en' | 'ja'>(strictVueI18n.locale) +expectType< + | 'en' + | 'ja' + | ('en' | 'ja')[] + | { [x in string]: PickupFallbackLocales<['en' | 'ja']>[] } + | false +>(strictVueI18n.fallbackLocale) +expectType<{ en: ResourceSchema; ja: ResourceSchema }>(strictVueI18n.messages) +expectType<{ en: {}; ja: {} }>(strictVueI18n.datetimeFormats) +expectType<{ en: {}; ja: {} }>(strictVueI18n.numberFormats) +expectType(strictVueI18n.t('nest.bar')) +expectType(strictVueI18n.t('nest', 'en')) +expectType(strictVueI18n.t('nest', 'en', [1])) +expectType(strictVueI18n.t('nest', 'en', { foo: 'test' })) +expectType(strictVueI18n.t('foo', [1])) +expectType(strictVueI18n.t('nest', { foo: 1 })) +expectType(strictVueI18n.tc('nest')) +expectType(strictVueI18n.tc('bar', 'en')) +expectType(strictVueI18n.tc('bar', ['foo'])) +expectType(strictVueI18n.tc('bar', { foo: 'foo' })) +expectType(strictVueI18n.tc('nest.bar', 1)) +expectType(strictVueI18n.tc('nest.bar', 1, ['bar'])) +expectType(strictVueI18n.tc('nest.bar', 1, { foo: 'bar' })) +expectType(strictVueI18n.te('errors', 'en')) +expectType<{ bar: string }>(strictVueI18n.tm('nest')) +expectType>(strictVueI18n.tm('errors')) +expectType(strictVueI18n.rt('foo')) +expectType( + strictVueI18n.getLocaleMessage('en') +) +expectType<{ japan: string }>( + strictVueI18n.getLocaleMessage<{ japan: string }>('japan') +) +strictVueI18n.setLocaleMessage('en', { + foo: 'foo', + nest: { + bar: 'bar' + }, + errors: ['error1'] +}) +strictVueI18n.setLocaleMessage<{ dio: string }>('jojo', { + dio: 'The world!' +}) +strictVueI18n.mergeLocaleMessage('en', { + bar: 'foo' +}) +strictVueI18n.mergeLocaleMessage<{ dio: string }>('en', { + dio: 'The world!' +}) + +const nullVueI18n = createVueI18n({}) +expectType(nullVueI18n.locale) +nullVueI18n.locale = 'en' +expectType(nullVueI18n.fallbackLocale) +nullVueI18n.fallbackLocale = 'fr'