Repository: kazupon/vue-i18n Branch: v8.x Commit: 76ab980d32a3 Files: 556 Total size: 5.2 MB Directory structure: gitextract_4s507kkn/ ├── .babelrc ├── .dependabot/ │ └── config.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .flowconfig ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report.yml │ │ ├── config.yml │ │ └── feature_request.yml │ └── workflows/ │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .mergify.yml ├── .npmignore ├── BACKERS.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── assets/ │ └── README.md ├── config/ │ ├── .eslintrc.js │ ├── banner.js │ ├── build.js │ ├── bundle.js │ ├── entry.js │ ├── karma-intl-plugin.js │ ├── karma.base.conf.js │ ├── karma.cover.conf.js │ ├── karma.sauce.conf.js │ ├── karma.unit.ci.conf.js │ ├── karma.unit.conf.js │ ├── nightwatch.conf.js │ ├── version.js │ └── webpack.dev.conf.js ├── decls/ │ ├── i18n.js │ └── module.js ├── dist/ │ ├── README.md │ ├── vue-i18n.common.js │ ├── vue-i18n.esm.browser.js │ ├── vue-i18n.esm.js │ └── vue-i18n.js ├── docs/ │ ├── 404.html │ ├── api/ │ │ └── index.html │ ├── assets/ │ │ ├── css/ │ │ │ └── 0.styles.c299b601.css │ │ └── js/ │ │ ├── 10.13689381.js │ │ ├── 11.aa13f293.js │ │ ├── 12.2b21ead8.js │ │ ├── 13.b3706367.js │ │ ├── 14.1819508a.js │ │ ├── 15.12152ec7.js │ │ ├── 16.c251473e.js │ │ ├── 17.311179bd.js │ │ ├── 18.a9d0f1d4.js │ │ ├── 19.d0288f09.js │ │ ├── 2.aa910774.js │ │ ├── 20.9de38e10.js │ │ ├── 21.65715f7c.js │ │ ├── 22.7b59f449.js │ │ ├── 23.11ece4c3.js │ │ ├── 24.dffd1071.js │ │ ├── 25.97b69148.js │ │ ├── 26.616f0814.js │ │ ├── 27.adf21b94.js │ │ ├── 28.6bf6d42b.js │ │ ├── 29.e8b42909.js │ │ ├── 3.5e63523d.js │ │ ├── 30.24889fb9.js │ │ ├── 31.57bd94a5.js │ │ ├── 32.14dc9bdc.js │ │ ├── 33.4d59e7bf.js │ │ ├── 34.adc9b2af.js │ │ ├── 35.4e6e5ad7.js │ │ ├── 36.7b1eb244.js │ │ ├── 37.6584773d.js │ │ ├── 38.7879de31.js │ │ ├── 39.f7496f35.js │ │ ├── 4.9735c0af.js │ │ ├── 40.b5b5dc72.js │ │ ├── 41.638a9c02.js │ │ ├── 42.222a2780.js │ │ ├── 43.b28cd4fc.js │ │ ├── 44.67f64685.js │ │ ├── 45.550cbef0.js │ │ ├── 46.5ab1c570.js │ │ ├── 47.d7b6ca42.js │ │ ├── 48.185bface.js │ │ ├── 49.1570bc72.js │ │ ├── 5.0b14e886.js │ │ ├── 50.0e187269.js │ │ ├── 51.c993f5c3.js │ │ ├── 52.d66b2adb.js │ │ ├── 53.21ae73c5.js │ │ ├── 54.cd4c9ca8.js │ │ ├── 55.caa71074.js │ │ ├── 56.c8f906b3.js │ │ ├── 57.388c89dc.js │ │ ├── 58.ad6e14bc.js │ │ ├── 59.3d64068b.js │ │ ├── 6.b88753f1.js │ │ ├── 60.12bfe8e9.js │ │ ├── 61.446dac92.js │ │ ├── 62.88ef088f.js │ │ ├── 63.b6e0a846.js │ │ ├── 64.f3210fad.js │ │ ├── 65.afac79e3.js │ │ ├── 66.d403fc3a.js │ │ ├── 67.0bffd98a.js │ │ ├── 7.eaeacaf7.js │ │ ├── 8.344bd519.js │ │ ├── 9.ec02aae8.js │ │ └── app.5b04e7b0.js │ ├── guide/ │ │ ├── component.html │ │ ├── datetime.html │ │ ├── directive.html │ │ ├── fallback.html │ │ ├── formatting.html │ │ ├── hot-reload.html │ │ ├── interpolation.html │ │ ├── lazy-loading.html │ │ ├── locale.html │ │ ├── messages.html │ │ ├── number.html │ │ ├── pluralization.html │ │ ├── sfc.html │ │ └── tooling.html │ ├── index.html │ ├── installation.html │ ├── introduction.html │ ├── legacy/ │ │ ├── index.html │ │ └── v5.html │ ├── ru/ │ │ ├── api/ │ │ │ └── index.html │ │ ├── guide/ │ │ │ ├── component.html │ │ │ ├── datetime.html │ │ │ ├── directive.html │ │ │ ├── fallback.html │ │ │ ├── formatting.html │ │ │ ├── hot-reload.html │ │ │ ├── interpolation.html │ │ │ ├── lazy-loading.html │ │ │ ├── locale.html │ │ │ ├── messages.html │ │ │ ├── number.html │ │ │ ├── pluralization.html │ │ │ ├── sfc.html │ │ │ └── tooling.html │ │ ├── index.html │ │ ├── installation.html │ │ ├── introduction.html │ │ ├── legacy/ │ │ │ ├── index.html │ │ │ └── v5.html │ │ └── started.html │ ├── started.html │ └── zh/ │ ├── api/ │ │ └── index.html │ ├── guide/ │ │ ├── component.html │ │ ├── datetime.html │ │ ├── directive.html │ │ ├── fallback.html │ │ ├── formatting.html │ │ ├── hot-reload.html │ │ ├── interpolation.html │ │ ├── lazy-loading.html │ │ ├── locale.html │ │ ├── messages.html │ │ ├── number.html │ │ ├── pluralization.html │ │ ├── sfc.html │ │ └── tooling.html │ ├── index.html │ ├── installation.html │ ├── introduction.html │ ├── legacy/ │ │ ├── index.html │ │ └── v5.html │ └── started.html ├── examples/ │ ├── .eslintrc.js │ ├── component/ │ │ └── index.html │ ├── datetime/ │ │ └── index.html │ ├── directive/ │ │ └── index.html │ ├── es-modules/ │ │ └── index.html │ ├── formatting/ │ │ ├── custom/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── babel.config.js │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── index.html │ │ │ └── src/ │ │ │ ├── App.vue │ │ │ ├── formatter.js │ │ │ ├── i18n.js │ │ │ └── main.js │ │ ├── escape-parameter-html/ │ │ │ └── index.html │ │ ├── html/ │ │ │ └── index.html │ │ ├── html-warning/ │ │ │ └── index.html │ │ ├── list/ │ │ │ └── index.html │ │ ├── named/ │ │ │ └── index.html │ │ └── rails/ │ │ └── index.html │ ├── hot/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── babel.config.js │ │ ├── package.json │ │ ├── public/ │ │ │ └── index.html │ │ └── src/ │ │ ├── App.vue │ │ ├── components/ │ │ │ └── HelloWorld.vue │ │ ├── i18n.js │ │ ├── locales/ │ │ │ ├── en.json │ │ │ └── ja.json │ │ └── main.js │ ├── integration/ │ │ └── contentful/ │ │ ├── .contentful.sample.json │ │ ├── .editorconfig │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── WHATS-NEXT.md │ │ ├── assets/ │ │ │ └── README.md │ │ ├── bin/ │ │ │ └── download-content-model.js │ │ ├── components/ │ │ │ ├── README.md │ │ │ ├── article-preview.vue │ │ │ ├── language-header.vue │ │ │ └── navigation.vue │ │ ├── docs/ │ │ │ └── GETTING-STARTED.md │ │ ├── layouts/ │ │ │ ├── README.md │ │ │ └── default.vue │ │ ├── locales/ │ │ │ ├── en-US.json │ │ │ └── ja.json │ │ ├── middleware/ │ │ │ └── README.md │ │ ├── nuxt.config.js │ │ ├── package.json │ │ ├── pages/ │ │ │ ├── README.md │ │ │ ├── blog/ │ │ │ │ ├── _slug.vue │ │ │ │ └── index.vue │ │ │ ├── index.vue │ │ │ └── tags/ │ │ │ └── _tag.vue │ │ ├── plugins/ │ │ │ ├── README.md │ │ │ ├── contentful.js │ │ │ └── i18n.js │ │ ├── static/ │ │ │ └── README.md │ │ └── store/ │ │ └── README.md │ ├── interpolation/ │ │ ├── places/ │ │ │ └── index.html │ │ └── slots/ │ │ └── index.html │ ├── module/ │ │ ├── nuxt/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── assets/ │ │ │ │ └── README.md │ │ │ ├── components/ │ │ │ │ ├── Logo.vue │ │ │ │ └── README.md │ │ │ ├── layouts/ │ │ │ │ ├── README.md │ │ │ │ └── default.vue │ │ │ ├── locales/ │ │ │ │ ├── en.json │ │ │ │ └── ja.json │ │ │ ├── middleware/ │ │ │ │ └── README.md │ │ │ ├── nuxt.config.js │ │ │ ├── package.json │ │ │ ├── pages/ │ │ │ │ ├── README.md │ │ │ │ └── index.vue │ │ │ ├── plugins/ │ │ │ │ └── README.md │ │ │ ├── static/ │ │ │ │ └── README.md │ │ │ └── store/ │ │ │ └── README.md │ │ ├── rollup/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── dist/ │ │ │ │ └── index.html │ │ │ ├── package.json │ │ │ ├── rollup.config.js │ │ │ └── src/ │ │ │ ├── App.vue │ │ │ └── index.js │ │ └── webpack/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── babel.config.js │ │ ├── package.json │ │ ├── public/ │ │ │ └── index.html │ │ ├── src/ │ │ │ ├── App.vue │ │ │ └── main.js │ │ └── vue.config.js │ ├── number/ │ │ └── index.html │ ├── number-formatting/ │ │ └── index.html │ ├── perf/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── compiler/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── babel.config.js │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── index.html │ │ │ ├── src/ │ │ │ │ ├── App.vue │ │ │ │ └── main.js │ │ │ └── vue.config.js │ │ ├── directive/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── babel.config.js │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── index.html │ │ │ ├── src/ │ │ │ │ ├── App.vue │ │ │ │ └── main.js │ │ │ └── vue.config.js │ │ ├── generate/ │ │ │ └── .gitkeep │ │ ├── index.js │ │ ├── method/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── babel.config.js │ │ │ ├── package.json │ │ │ ├── public/ │ │ │ │ └── index.html │ │ │ ├── src/ │ │ │ │ ├── App.vue │ │ │ │ └── main.js │ │ │ └── vue.config.js │ │ ├── package.json │ │ └── plain/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── babel.config.js │ │ ├── package.json │ │ ├── public/ │ │ │ └── index.html │ │ ├── src/ │ │ │ ├── App.vue │ │ │ └── main.js │ │ └── vue.config.js │ ├── sfc/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── babel.config.js │ │ ├── package.json │ │ ├── public/ │ │ │ └── index.html │ │ ├── src/ │ │ │ ├── App.vue │ │ │ └── main.js │ │ └── vue.config.js │ ├── ssr/ │ │ ├── hn2/ │ │ │ ├── .babelrc │ │ │ ├── .gitignore │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── build/ │ │ │ │ ├── setup-dev-server.js │ │ │ │ ├── vue-loader.config.js │ │ │ │ ├── webpack.base.config.js │ │ │ │ ├── webpack.client.config.js │ │ │ │ └── webpack.server.config.js │ │ │ ├── manifest.json │ │ │ ├── package.json │ │ │ ├── server.js │ │ │ └── src/ │ │ │ ├── App.vue │ │ │ ├── api/ │ │ │ │ ├── create-api-client.js │ │ │ │ ├── create-api-server.js │ │ │ │ └── index.js │ │ │ ├── app.js │ │ │ ├── components/ │ │ │ │ ├── Comment.vue │ │ │ │ ├── Item.vue │ │ │ │ ├── ProgressBar.vue │ │ │ │ └── Spinner.vue │ │ │ ├── entry-client.js │ │ │ ├── entry-server.js │ │ │ ├── i18n/ │ │ │ │ ├── en-US.json │ │ │ │ ├── index.js │ │ │ │ └── ja-JP.json │ │ │ ├── index.template.html │ │ │ ├── router/ │ │ │ │ └── index.js │ │ │ ├── store/ │ │ │ │ ├── actions.js │ │ │ │ ├── getters.js │ │ │ │ ├── index.js │ │ │ │ └── mutations.js │ │ │ ├── util/ │ │ │ │ ├── filters.js │ │ │ │ └── title.js │ │ │ ├── util.js │ │ │ └── views/ │ │ │ ├── CreateListView.js │ │ │ ├── ItemList.vue │ │ │ ├── ItemView.vue │ │ │ └── UserView.vue │ │ └── nuxt/ │ │ ├── .editorconfig │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── README.md │ │ ├── assets/ │ │ │ └── README.md │ │ ├── components/ │ │ │ ├── Logo.vue │ │ │ └── README.md │ │ ├── layouts/ │ │ │ ├── README.md │ │ │ └── default.vue │ │ ├── locales/ │ │ │ ├── en.json │ │ │ └── ja.json │ │ ├── middleware/ │ │ │ └── README.md │ │ ├── nuxt.config.js │ │ ├── package.json │ │ ├── pages/ │ │ │ ├── README.md │ │ │ └── index.vue │ │ ├── plugins/ │ │ │ ├── README.md │ │ │ └── i18n.js │ │ ├── static/ │ │ │ └── README.md │ │ └── store/ │ │ └── README.md │ ├── started/ │ │ └── index.html │ └── storybook/ │ ├── .babelrc │ ├── .gitignore │ ├── .storybook/ │ │ ├── addons.js │ │ └── config.js │ ├── README.md │ ├── index.html │ ├── package.json │ ├── src/ │ │ ├── App.vue │ │ ├── main.js │ │ └── stories/ │ │ ├── MyButton.vue │ │ ├── Welcome.vue │ │ └── index.js │ └── webpack.config.js ├── gitbook/ │ ├── LANGS.md │ ├── book.json │ ├── en/ │ │ ├── SUMMARY.md │ │ ├── api.md │ │ ├── component.md │ │ ├── datetime.md │ │ ├── directive.md │ │ ├── fallback.md │ │ ├── formatting.md │ │ ├── gotchas.md │ │ ├── hot-reload.md │ │ ├── installation.md │ │ ├── interpolation.md │ │ ├── lazy-loading.md │ │ ├── messages.md │ │ ├── migrations.md │ │ ├── number.md │ │ ├── pluralization.md │ │ ├── sfc.md │ │ └── started.md │ └── old/ │ └── README.md ├── package.json ├── scripts/ │ ├── docs-deploy.sh │ └── remove.sh ├── ship.config.js ├── src/ │ ├── components/ │ │ ├── interpolation.js │ │ └── number.js │ ├── directive.js │ ├── extend.js │ ├── format.js │ ├── index.js │ ├── install.js │ ├── mixin.js │ ├── path.js │ └── util.js ├── test/ │ ├── .eslintrc.js │ ├── e2e/ │ │ ├── runner.js │ │ └── test/ │ │ ├── component.js │ │ ├── datetime.js │ │ ├── directive.js │ │ ├── formatting_html.js │ │ ├── formatting_list.js │ │ ├── formatting_named.js │ │ ├── formatting_rails.js │ │ ├── interpolation.js │ │ ├── number.js │ │ ├── number_formatting.js │ │ └── started.js │ ├── helpers/ │ │ ├── entry.js │ │ ├── imports.js │ │ └── util.js │ └── unit/ │ ├── basic.test.js │ ├── component.test.js │ ├── custom_blocks.test.js │ ├── datetime.test.js │ ├── directive.test.js │ ├── escape_parameter_html.test.js │ ├── fallback.test.js │ ├── fixture/ │ │ ├── datetime.js │ │ ├── index.js │ │ └── number.js │ ├── format.test.js │ ├── format_custom.test.js │ ├── index.html │ ├── index.js │ ├── interpolation.test.js │ ├── issues.test.js │ ├── message.test.js │ ├── message_function.test.js │ ├── missing.test.js │ ├── mixin.test.js │ ├── number.test.js │ ├── number_component.test.js │ ├── path.test.js │ ├── post_translation.test.js │ ├── silent.test.js │ └── warn_html_in_message.test.js ├── types/ │ ├── .eslintrc.js │ ├── index.d.ts │ ├── test/ │ │ ├── component.ts │ │ └── index.ts │ └── tsconfig.json ├── vetur/ │ ├── attributes.json │ └── tags.json └── vuepress/ ├── .vuepress/ │ ├── components/ │ │ └── sponsor-button.vue │ ├── config.js │ └── theme/ │ ├── Layout.vue │ ├── components/ │ │ └── CarbonAds.vue │ └── index.js ├── README.md ├── api/ │ └── README.md ├── guide/ │ ├── component.md │ ├── datetime.md │ ├── directive.md │ ├── fallback.md │ ├── formatting.md │ ├── hot-reload.md │ ├── interpolation.md │ ├── lazy-loading.md │ ├── locale.md │ ├── messages.md │ ├── number.md │ ├── pluralization.md │ ├── sfc.md │ └── tooling.md ├── installation.md ├── introduction.md ├── pt/ │ ├── README.md │ ├── api/ │ │ └── README.md │ ├── guide/ │ │ ├── component.md │ │ ├── datetime.md │ │ ├── directive.md │ │ ├── fallback.md │ │ ├── formatting.md │ │ ├── hot-reload.md │ │ ├── interpolation.md │ │ ├── lazy-loading.md │ │ ├── locale.md │ │ ├── messages.md │ │ ├── number.md │ │ ├── pluralization.md │ │ ├── sfc.md │ │ └── tooling.md │ ├── installation.md │ ├── introduction.md │ └── started.md ├── ru/ │ ├── README.md │ ├── api/ │ │ └── README.md │ ├── guide/ │ │ ├── component.md │ │ ├── datetime.md │ │ ├── directive.md │ │ ├── fallback.md │ │ ├── formatting.md │ │ ├── hot-reload.md │ │ ├── interpolation.md │ │ ├── lazy-loading.md │ │ ├── locale.md │ │ ├── messages.md │ │ ├── number.md │ │ ├── pluralization.md │ │ ├── sfc.md │ │ └── tooling.md │ ├── installation.md │ ├── introduction.md │ └── started.md ├── started.md └── zh/ ├── README.md ├── api/ │ └── README.md ├── guide/ │ ├── component.md │ ├── datetime.md │ ├── directive.md │ ├── fallback.md │ ├── formatting.md │ ├── hot-reload.md │ ├── interpolation.md │ ├── lazy-loading.md │ ├── locale.md │ ├── messages.md │ ├── number.md │ ├── pluralization.md │ ├── sfc.md │ └── tooling.md ├── installation.md ├── introduction.md └── started.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "presets": ["@vue/app"], "plugins": [ "@babel/plugin-proposal-class-properties", "@babel/plugin-syntax-flow", "@babel/plugin-transform-flow-strip-types", "babel-plugin-espower" ], "env": { "test": { "plugins": ["istanbul"], "presets": ["power-assert"] } }, "ignore": [ "dist/*.js" ] } ================================================ FILE: .dependabot/config.yml ================================================ version: 1 update_configs: - package_manager: "javascript" directory: "/" update_schedule: "weekly" ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 indent_style = space indent_size = 2 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true ================================================ FILE: .eslintignore ================================================ config/*.js ================================================ FILE: .eslintrc.js ================================================ module.exports = { root: true, parserOptions: { ecmaVersion: 2018, }, plugins: ['flowtype', 'ie11', 'no-autofix'], extends: [ 'plugin:flowtype/recommended' ], rules: { 'ie11/no-collection-args': [ 'error' ], 'ie11/no-for-in-const': [ 'error' ], 'ie11/no-loop-func': [ 'warn' ], 'ie11/no-weak-collections': [ 'error' ], 'prefer-const': 'off', 'no-autofix/prefer-const': 'warn', 'object-curly-spacing': ['error', 'always'], 'no-multiple-empty-lines': ['error', { max: 2, maxBOF: 1 }] } } ================================================ FILE: .flowconfig ================================================ [ignore] .*/node_modules/.* .*/docs/.* .*/test/.* .*/types/.* .*/config/.* .*/examples/.* [include] [libs] decls [options] unsafe.enable_getters_and_setters=true suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: kazupon patreon: # kazupon open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel issuehunt: # kazupon custom: # Replace with a single custom sponsorship URL ================================================ FILE: .github/ISSUE_TEMPLATE/bug-report.yml ================================================ name: "\U0001F41E Bug report" description: Report an issue labels: ['Status: Review Needed'] body: - type: markdown attributes: value: | Thanks for taking the time to fill out this bug report! - type: textarea id: bug-description attributes: label: Reporting a bug? description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks! placeholder: Bug description validations: required: true - type: textarea id: expected-behavior attributes: label: Expected behavior description: A clear and concise description of what you expected to happen. placeholder: Expected behavior validations: required: true - type: textarea id: reproduction attributes: label: Reproduction description: | Please provide a link to a repo that can reproduce the problem you ran into. https://jsfiddle.net/kazupon/rn724baz/3/ A reproduction is required unless you are absolutely sure that the issue is obvious and the provided information is enough to understand the problem. If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "Status: Need More Info" label. If no reproduction is provided after 5 days, it will be closed. placeholder: Reproduction validations: required: true - type: textarea id: system-info attributes: label: System Info description: Output of `npx envinfo --system --npmPackages '{vue*,@vue/*,vue-i18n*,@intlify/*,vite*,@vitejs/*}' --binaries --browsers` render: shell placeholder: System, Binaries, Browsers validations: required: true - type: textarea id: screenshot attributes: label: Screenshot description: If applicable, add screenshots to help explain your problem placeholder: screnshot - type: textarea id: additional-context attributes: label: Additional context description: Add any other context about the problem here placeholder: more context here - type: checkboxes id: checkboxes attributes: label: Validations description: Before submitting the issue, please make sure you do the following options: - label: Read the [Contributing Guidelines](https://github.com/kazupon/vue-i18n/blob/v8.x/CONTRIBUTING.md) required: true - label: Read the [Documentation](https://kazupon.github.io/vue-i18n/) required: true - label: Check that there isn't [already an issue](https://github.com/kazupon/vue-i18n/issues) that reports the same bug to avoid creating a duplicate. required: true - label: Check that this is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/kazupon/vue-i18n/discussions) required: true - label: The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug. required: true ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Discord Chat at Vue Land url: https://chat.vuejs.org/ about: For simple beginner questions, you can get quick answers from Discord - name: Questions & Discussions url: https://github.com/kazupon/vue-i18n/discussions about: Use GitHub discussions for message-board style questions and discussions. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.yml ================================================ name: "\U0001F680 New feature proposal" description: Propose a new feature labels: ['Status: Proposal'] body: - type: markdown attributes: value: | Thanks for your interest in the project and taking the time to fill out this feature report! - type: textarea id: feature-description attributes: label: Clear and concise description of the problem description: If you intend to submit a PR for this issue, tell us in the description. Thanks! validations: required: true - type: textarea id: suggested-solution attributes: label: Suggested solution description: 'In module [xy] we could provide following implementation...' validations: required: true - type: textarea id: alternative attributes: label: Alternative description: Clear and concise description of any alternative solutions or features you've considered. - type: textarea id: additional-context attributes: label: Additional context description: Any other context or screenshots about the feature request here. - type: checkboxes id: checkboxes attributes: label: Validations description: Before submitting the issue, please make sure you do the following options: - label: Read the [Contributing Guidelines](https://github.com/kazupon/vue-i18n/blob/v8.x/CONTRIBUTING.md) required: true - label: Read the [Documentation](https://kazupon.github.io/vue-i18n/) required: true - label: Check that there isn't already an issue that request the same feature to avoid creating a duplicate. required: true ================================================ FILE: .github/workflows/ci.yml ================================================ name: Test on: push: branches-ignore: - gh-pages - releases/** pull_request: branches-ignore: - gh-pages - releases/** types: - opened - synchronize - reopened env: CI: true TZ: Asia/Tokyo CHROME_BIN: /usr/bin/google-chrome jobs: build: name: "Build on Node.js ${{ matrix.node }} OS: ${{matrix.os}}" runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest] node: [18] steps: - name: Checkout codes uses: actions/checkout@v4 - name: Setup node uses: actions/setup-node@v3 with: node-version: ${{ matrix.node}} cache: 'yarn' - name: Install run: yarn install --frozen-lockfile - name: Build run: yarn build - name: Cache dist uses: actions/cache@v3 with: path: dist key: ${{ matrix.os }}-node-v${{ matrix.node }}-${{ github.sha }} lint: name: "Lint on Node.js ${{ matrix.node }} OS: ${{matrix.os}}" runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest] node: [18] steps: - name: Checkout codes uses: actions/checkout@v3 - name: Setup node uses: actions/setup-node@v3 with: node-version: ${{ matrix.node }} cache: 'yarn' - name: Install run: yarn install --frozen-lockfile - name: Lint run: yarn lint test: needs: - build name: "Test on Node.js ${{ matrix.node }} OS: ${{matrix.os}}" runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest] node: [18] steps: - name: Setup google chrome uses: browser-actions/setup-chrome@latest with: chrome-version: stable - run: chrome --version - name: Setup firefox uses: browser-actions/setup-firefox@latest with: firefox-version: latest - run: firefox --version - name: Checkout codes uses: actions/checkout@v3 - name: Setup node uses: actions/setup-node@v3 with: node-version: ${{ matrix.node }} cache: 'yarn' - name: Install run: yarn install --frozen-lockfile - name: Flow Types run: yarn flow - name: TS Types run: yarn test:types - name: Test Coverage run: yarn test:cover - name: Unit tests run: yarn test:unit:ci - name: Restore dist cache uses: actions/cache@v3 with: path: dist key: ${{ matrix.os }}-node-v${{ matrix.node }}-${{ github.sha }} - name: E2E Tests run: xvfb-run --auto-servernum yarn test:e2e ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: pull_request: types: - closed jobs: release: name: Release if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release') runs-on: Ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: registry-url: "https://registry.npmjs.org" - run: git switch master - run: | if [ -f "yarn.lock" ]; then yarn install else npm install fi - run: npm run release:trigger env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} SLACK_INCOMING_HOOK: ${{ secrets.SLACK_INCOMING_HOOK }} ================================================ FILE: .gitignore ================================================ lib coverage dist/*.gz docs/_book test/e2e/report test/e2e/screenshots node_modules vuepress/.vuepress/dist .DS_Store .idea .vscode .env *.log *.swp *~ ================================================ FILE: .mergify.yml ================================================ pull_request_rules: - name: merge pull requests from dependabot if CI passes conditions: - author=dependabot[bot] - status-success=ci/circleci actions: merge: method: merge ================================================ FILE: .npmignore ================================================ .* *.log *.swp *.yml coverage docs/_book config dist/*.map lib test ================================================ FILE: BACKERS.md ================================================ # Backers You can join them in supporting vue-i18n & Intlify project development by [pledging on GitHub](https://github.com/sponsors/kazupon)! Backers in the same pledge level appear in the order of pledge date.

Platinum Sponsors

Special Sponsors

Gold Sponsors

[It could be you!](https://www.patreon.com/bePatron?c=1597144&patAmt=500.0)

Sliver Sponsors

[It could be you!](https://www.patreon.com/bePatron?c=1597144&patAmt=250.0)

Bronze Sponsors

- [Illyism](https://github.com/Illyism) [It could be you!](https://www.patreon.com/bePatron?c=1597144&patAmt=100.0)

Awesome Supporters

- [Shinya Katayama](https://github.com/ktsn) - [Jeffrey Rennie](https://github.com/surferjeff) [It could be you!](https://www.patreon.com/bePatron?c=1597144&patAmt=20.0) ================================================ FILE: CHANGELOG.md ================================================ ## v8.28.2 (2022-10-31) #### :bug: Bug Fixes * [#1575](https://github.com/kazupon/vue-i18n/pull/1575) fix(datetime): fixing calender typo ([@MKhasib](https://github.com/MKhasib)) #### Committers: 1 - Majd Khasib ([@MKhasib](https://github.com/MKhasib)) ## v8.28.1 (2022-10-31) #### :zap: Improved Features * [#1572](https://github.com/kazupon/vue-i18n/pull/1572) fix(datetime): support all key format object (kazupon#1571) ([@majdkhasib](https://github.com/majdkhasib)) #### Committers: 1 - [@majdkhasib](https://github.com/majdkhasib) ## v8.28.0 (2022-10-30) #### :star: New Features * [#1569](https://github.com/kazupon/vue-i18n/pull/1569) fix(datetime): $d supports key format object (#1502) ([@horitaka](https://github.com/horitaka)) #### Committers: 1 - [@horitaka](https://github.com/horitaka) ## v8.27.2 (2022-06-20) #### :bug: Bug Fixes * [#1512](https://github.com/kazupon/vue-i18n/pull/1512) Allow modifiers names in pascalCase ([@nofacez](https://github.com/nofacez)) #### :pencil: Documentation * [#1505](https://github.com/kazupon/vue-i18n/pull/1505) Update interpolation.md ([@victor1014](https://github.com/victor1014)) #### Committers: 2 - Mikhail ([@nofacez](https://github.com/nofacez)) - Victor ([@victor1014](https://github.com/victor1014)) ## v8.27.1 (2022-03-29) #### :zap: Improved Features * [#1488](https://github.com/kazupon/vue-i18n/pull/1488) Allow colon in locale message keys used in linked locale messages ([@ax-jmckenzie](https://github.com/ax-jmckenzie)) #### :pencil: Documentation * [#1462](https://github.com/kazupon/vue-i18n/pull/1462) Restrict version to 8 for installing npm package ([@arpi17](https://github.com/arpi17)) #### Committers: 2 - James McKenzie ([@ax-jmckenzie](https://github.com/ax-jmckenzie)) - Árpád Illyés ([@arpi17](https://github.com/arpi17)) ## v8.27.0 (2022-01-21) #### :star: New Features * [#1441](https://github.com/kazupon/vue-i18n/pull/1441) feat: add a new option fallbackRootWithEmptyString ([@PeterAlfredLee](https://github.com/PeterAlfredLee)) #### Committers: 1 - Lee ([@PeterAlfredLee](https://github.com/PeterAlfredLee)) ## v8.26.8 (2021-12-28) #### :zap: Improved Features * [#1429](https://github.com/kazupon/vue-i18n/pull/1429) improvement: type definition order for vue-i18n-bridge integration ([@kazupon](https://github.com/kazupon)) #### :pencil: Documentation * [#1416](https://github.com/kazupon/vue-i18n/pull/1416) Update links to i18n Ally in tooling docs ([@FloEdelmann](https://github.com/FloEdelmann)) #### Committers: 2 - Flo Edelmann ([@FloEdelmann](https://github.com/FloEdelmann)) - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.26.7 (2021-11-02) #### :bug: Bug Fixes * [#1411](https://github.com/kazupon/vue-i18n/pull/1411) fix: cannot work watchLocale on vue-i18n-bridge ([@kazupon](https://github.com/kazupon)) #### Committers: 1 - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.26.6 (2021-11-02) #### :zap: Improved Features * [#1409](https://github.com/kazupon/vue-i18n/pull/1409) improvement: notify locale changing to composer ([@kazupon](https://github.com/kazupon)) #### Committers: 1 - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.26.5 (2021-10-01) #### :zap: Improved Features * [#1396](https://github.com/kazupon/vue-i18n/pull/1396) Revert "improvement: $i18n property deinition for vue-i18n-bridge" ([@kazupon](https://github.com/kazupon)) #### Committers: 1 - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.26.4 (2021-10-01) #### :zap: Improved Features * [#1394](https://github.com/kazupon/vue-i18n/pull/1394) improvement: $i18n property deinition for vue-i18n-bridge ([@kazupon](https://github.com/kazupon)) #### Committers: 1 - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.26.3 (2021-09-30) #### :zap: Improved Features * [#1392](https://github.com/kazupon/vue-i18n/pull/1392) improvement: vue-i18n-loader bridge mode ([@kazupon](https://github.com/kazupon)) #### Committers: 1 - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.26.2 (2021-09-28) #### :zap: Improved Features * [#1390](https://github.com/kazupon/vue-i18n/pull/1390) improvement: add flags for vue-i18n-bridge ([@kazupon](https://github.com/kazupon)) #### Committers: 1 - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.26.1 (2021-09-23) #### :zap: Improved Features * [#1388](https://github.com/kazupon/vue-i18n/pull/1388) feat: add sync property ([@kazupon](https://github.com/kazupon)) #### Committers: 1 - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.26.0 (2021-09-22) #### :star: New Features * [#1385](https://github.com/kazupon/vue-i18n/pull/1385) feat: support for vue-i18n-bridge ([@kazupon](https://github.com/kazupon)) #### Committers: 1 - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.25.1 (2021-09-13) #### :bug: Bug Fixes * [#1381](https://github.com/kazupon/vue-i18n/pull/1381) fix: linked messages containing slash symbol ([@Lamberthassel](https://github.com/Lamberthassel)) #### :lock: Security Fixes * [#1353](https://github.com/kazupon/vue-i18n/pull/1353) build(deps): [security] bump tar from 6.0.2 to 6.1.3 ([@dependabot-preview[bot]](https://github.com/apps/dependabot-preview)) #### :pencil: Documentation * [#1323](https://github.com/kazupon/vue-i18n/pull/1323) Added version notice for custom pluralizations ([@ingria](https://github.com/ingria)) #### Committers: 3 - Igor Nabebin ([@Lamberthassel](https://github.com/Lamberthassel)) - Ingria ([@ingria](https://github.com/ingria)) - [@dependabot-preview[bot]](https://github.com/apps/dependabot-preview) ## v8.25.0 (2021-07-16) #### :star: New Features * [#1271](https://github.com/kazupon/vue-i18n/pull/1271) feat: Added linked, locale, formatter, values, path to MessageContext ([@fabis94](https://github.com/fabis94)) #### :bug: Bug Fixes * [#1321](https://github.com/kazupon/vue-i18n/pull/1321) fix broken mergeLocaleMessage ([@phonezawphyo](https://github.com/phonezawphyo)) #### Committers: 3 - [@dependabot-preview[bot]](https://github.com/apps/dependabot-preview) - [@fabis94](https://github.com/fabis94) - [@phonezawphyo](https://github.com/phonezawphyo) ## v8.24.5 (2021-06-19) #### :bug: Bug Fixes * [#1278](https://github.com/kazupon/vue-i18n/pull/1278) fix: Ensure that the order at desroy is the same as before change to `Set` from `Array` ([@kazupon](https://github.com/kazupon)) #### :zap: Improved Features * [#1194](https://github.com/kazupon/vue-i18n/pull/1194) fix: add locale arg type i18n.n method type signature ([@alireza4050](https://github.com/alireza4050)) #### :pencil: Documentation * [#1273](https://github.com/kazupon/vue-i18n/pull/1273) docs(api:zh): update the Chinese docs of "API" ([@baboon-king](https://github.com/baboon-king)) * [#1267](https://github.com/kazupon/vue-i18n/pull/1267) Documentation - Update @kazupon/vue-i18n-loader to @intlify/vue-i18n-loader ([@fbigand](https://github.com/fbigand)) * [#1258](https://github.com/kazupon/vue-i18n/pull/1258) Grammar fix for Installing vue-i18n-loader ([@TerabyteTiger](https://github.com/TerabyteTiger)) #### Committers: 5 - Alireza Asgharizadeh ([@alireza4050](https://github.com/alireza4050)) - BaboonKing ([@baboon-king](https://github.com/baboon-king)) - Tyler V ([@TerabyteTiger](https://github.com/TerabyteTiger)) - [@fbigand](https://github.com/fbigand) - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.24.4 (2021-04-28) #### :bug: Bug Fixes * [#1192](https://github.com/kazupon/vue-i18n/pull/1192) Revert "fix: i18n reference to root causes memory leak (#1044)" ([@kazupon](https://github.com/kazupon)) #### Committers: 2 - Alexander Sokolov ([@Alex-Sokolov](https://github.com/Alex-Sokolov)) - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.24.3 (2021-04-09) #### :bug: Bug Fixes * [#1175](https://github.com/kazupon/vue-i18n/pull/1175) [perf] Use Set so lookup of _dataListeners can be O(1) ([@exoego](https://github.com/exoego)) #### :chart_with_upwards_trend: Performance Fixes * [#1175](https://github.com/kazupon/vue-i18n/pull/1175) [perf] Use Set so lookup of _dataListeners can be O(1) ([@exoego](https://github.com/exoego)) #### Committers: 1 - TATSUNO Yasuhiro ([@exoego](https://github.com/exoego)) ## v8.24.2 (2021-03-23) #### :bug: Bug Fixes * [#1151](https://github.com/kazupon/vue-i18n/pull/1151) fix: i18n reference to root causes memory leak (#1044) ([@lzxb](https://github.com/lzxb)) #### :pencil: Documentation * [#1152](https://github.com/kazupon/vue-i18n/pull/1152) $tc return type ([@Glandos](https://github.com/Glandos)) #### Committers: 2 - Glandos ([@Glandos](https://github.com/Glandos)) - 狼族小狈 ([@lzxb](https://github.com/lzxb)) ## v8.24.1 (2021-03-11) #### :bug: Bug Fixes * [#1145](https://github.com/kazupon/vue-i18n/pull/1145) Allowing paths to be null for lookups. ([@martinheidegger](https://github.com/martinheidegger)) #### Committers: 1 - Martin Heidegger ([@martinheidegger](https://github.com/martinheidegger)) ## v8.24.0 (2021-03-08) #### :star: New Features * [#1139](https://github.com/kazupon/vue-i18n/pull/1139) experimental: add meta info for intlify tools ([@kazupon](https://github.com/kazupon)) #### Committers: 1 - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.23.0 (2021-02-24) #### :zap: Improved Features * [#1118](https://github.com/kazupon/vue-i18n/pull/1118) Fix wrong LocaleMatcher type for TS 4.2 ('best-fit' to 'best fit'). ([@mpawelski](https://github.com/mpawelski)) #### :pencil: Documentation * [#1124](https://github.com/kazupon/vue-i18n/pull/1124) Add documentation for directive `choice` argument ([@Gameghostify](https://github.com/Gameghostify)) * [#1114](https://github.com/kazupon/vue-i18n/pull/1114) Update tooling.md ([@fanlinqiang](https://github.com/fanlinqiang)) * [#1104](https://github.com/kazupon/vue-i18n/pull/1104) Documentation improvemation ([@lucasferreiralimax](https://github.com/lucasferreiralimax)) #### Committers: 4 - Lucas ([@lucasferreiralimax](https://github.com/lucasferreiralimax)) - Mariusz Pawelski ([@mpawelski](https://github.com/mpawelski)) - [@Gameghostify](https://github.com/Gameghostify) - [@fanlinqiang](https://github.com/fanlinqiang) ## v8.22.4 (2021-01-16) #### :chart_with_upwards_trend: Performance Fixes * [#1101](https://github.com/kazupon/vue-i18n/pull/1101) Improve performance of mergeLocaleMessage (#1099) ([@cslee](https://github.com/cslee)) #### Committers: 2 - Eric Lee ([@cslee](https://github.com/cslee)) - Lucas ([@lucasferreiralimax](https://github.com/lucasferreiralimax)) ## v8.22.3 (2021-01-08) #### :zap: Improved Features * [#1096](https://github.com/kazupon/vue-i18n/pull/1096) @types: Fixed too narrow option types in n() and d() methods ([@gehlert](https://github.com/gehlert)) #### Committers: 3 - MASONGZHI ([@masongzhi](https://github.com/masongzhi)) - TATSUNO Yasuhiro ([@exoego](https://github.com/exoego)) - [@gehlert](https://github.com/gehlert) ## v8.22.2 (2020-11-18) #### :bug: Bug Fixes * [#1048](https://github.com/kazupon/vue-i18n/pull/1048) fix: number format keys missing compared the Intl.Number object ([@jevillard](https://github.com/jevillard)) #### Committers: 2 - Alexander Sokolov ([@Alex-Sokolov](https://github.com/Alex-Sokolov)) - Jérémy Villard ([@jevillard](https://github.com/jevillard)) ## v8.22.1 (2020-10-19) #### :boom: Breaking Change * [#1020](https://github.com/kazupon/vue-i18n/pull/1020) escapeParameterHtml flag: Don't escape ampersand #1019 ([@gardarh](https://github.com/gardarh)) #### Committers: 1 - Gardar Hauksson ([@gardarh](https://github.com/gardarh)) ## v8.22.0 (2020-10-06) #### :star: New Features * [#1009](https://github.com/kazupon/vue-i18n/pull/1009) Add escapeParameterHtml parameter. #1002 ([@gardarh](https://github.com/gardarh)) #### :bug: Bug Fixes * [#1003](https://github.com/kazupon/vue-i18n/pull/1003) Bugfix in index.js: wrong if condition in fetchChoice ([@cimchd](https://github.com/cimchd)) #### Committers: 4 - Andreas ([@devmount](https://github.com/devmount)) - Gardar Hauksson ([@gardarh](https://github.com/gardarh)) - Raffaele Pizzari ([@pixari](https://github.com/pixari)) - [@cimchd](https://github.com/cimchd) ## v8.21.1 (2020-09-11) #### :bug: Bug Fixes * [#995](https://github.com/kazupon/vue-i18n/pull/995) Update mixin.js ([@ferencbeutel4711](https://github.com/ferencbeutel4711)) #### Committers: 1 - Ferenc Beutel ([@ferencbeutel4711](https://github.com/ferencbeutel4711)) ## v8.21.0 (2020-08-13) #### :star: New Features * [#972](https://github.com/kazupon/vue-i18n/pull/972) feat: message function ([@kazupon](https://github.com/kazupon)) #### :pencil: Documentation * [#961](https://github.com/kazupon/vue-i18n/pull/961) Update link to Formatter Interface ([@JohJohan](https://github.com/JohJohan)) #### Committers: 3 - Alexander Sokolov ([@Alex-Sokolov](https://github.com/Alex-Sokolov)) - Johan ([@JohJohan](https://github.com/JohJohan)) - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.20.0 (2020-07-30) #### :star: New Features * [#959](https://github.com/kazupon/vue-i18n/pull/959) i18n-n component local components passing ([@kazupon](https://github.com/kazupon)) * [#928](https://github.com/kazupon/vue-i18n/pull/928) :zap: improvement(interpolation): enable passage of local components to tag prop ([@vhoyer](https://github.com/vhoyer)) #### Committers: 2 - Vinícius Hoyer ([@vhoyer](https://github.com/vhoyer)) - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.19.0 (2020-07-25) #### :star: New Features * [#942](https://github.com/kazupon/vue-i18n/pull/942) Add vetur support for tags and attributes ([@phiter](https://github.com/phiter)) #### :pencil: Documentation * [#925](https://github.com/kazupon/vue-i18n/pull/925) Added missing quote ([@fschlag](https://github.com/fschlag)) * [#921](https://github.com/kazupon/vue-i18n/pull/921) Add lost pluralizationRules option to documentation ([@AleksandrSl](https://github.com/AleksandrSl)) * [#920](https://github.com/kazupon/vue-i18n/pull/920) Make link to API and Guide top level ([@AleksandrSl](https://github.com/AleksandrSl)) #### Committers: 3 - Aleksandr ([@AleksandrSl](https://github.com/AleksandrSl)) - Florian Schlag ([@fschlag](https://github.com/fschlag)) - Phiter Fernandes ([@phiter](https://github.com/phiter)) ## v8.18.2 (2020-06-08) #### :zap: Improved Features * [#917](https://github.com/kazupon/vue-i18n/pull/917) fix: improve IVueI18n interface ([@kazupon](https://github.com/kazupon)) #### :pencil: Documentation * [#902](https://github.com/kazupon/vue-i18n/pull/902) docs: [RU] Translation update ([@Alex-Sokolov](https://github.com/Alex-Sokolov)) * [#901](https://github.com/kazupon/vue-i18n/pull/901) docs: (zh) inverse $d $n ([@stan-chen](https://github.com/stan-chen)) #### Committers: 4 - Alexander Sokolov ([@Alex-Sokolov](https://github.com/Alex-Sokolov)) - Rafał Chłodnicki ([@rchl](https://github.com/rchl)) - Stanley Chen ([@stan-chen](https://github.com/stan-chen)) - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.18.1 (2020-05-27) #### :bug: Bug Fixes * [#896](https://github.com/kazupon/vue-i18n/pull/896) Fix flow types and enable flow types testing on CI ([@rchl](https://github.com/rchl)) #### Committers: 1 - Rafał Chłodnicki ([@rchl](https://github.com/rchl)) ## v8.18.0 (2020-05-26) #### :star: New Features * [#892](https://github.com/kazupon/vue-i18n/pull/892) Add onComponentInstanceCreated constructor option ([@rchl](https://github.com/rchl)) #### :zap: Improved Features * [#890](https://github.com/kazupon/vue-i18n/pull/890) chore: set up linting for typescript definitions ([@rchl](https://github.com/rchl)) #### Committers: 1 - Rafał Chłodnicki ([@rchl](https://github.com/rchl)) ## v8.17.7 (2020-05-19) #### :bug: Bug Fixes * [#882](https://github.com/kazupon/vue-i18n/pull/882) fix v-t pluralisation when choice is 0 ([@mikejacoutot](https://github.com/mikejacoutot)) #### Committers: 1 - [@mikejacoutot](https://github.com/mikejacoutot) ## v8.17.6 (2020-05-15) #### :bug: Bug Fixes * [#880](https://github.com/kazupon/vue-i18n/pull/880) Don't delete _i18n in beforeDestroy ([@danimoh](https://github.com/danimoh)) #### :zap: Improved Features * [#878](https://github.com/kazupon/vue-i18n/pull/878) Allow component interpolation without root element ([@danimoh](https://github.com/danimoh)) #### :pencil: Documentation * [#875](https://github.com/kazupon/vue-i18n/pull/875) Add new 3rd party tool ([@danigayosog](https://github.com/danigayosog)) * [#872](https://github.com/kazupon/vue-i18n/pull/872) docs: fixes ([@Alex-Sokolov](https://github.com/Alex-Sokolov)) * [#871](https://github.com/kazupon/vue-i18n/pull/871) update pluralization.md ([@Timibadass](https://github.com/Timibadass)) #### Committers: 4 - Alexander Sokolov ([@Alex-Sokolov](https://github.com/Alex-Sokolov)) - Daniel ([@danigayosog](https://github.com/danigayosog)) - Timi Omoyeni ([@Timibadass](https://github.com/Timibadass)) - [@danimoh](https://github.com/danimoh) ## v8.17.5 (2020-05-10) #### :bug: Bug Fixes * [#869](https://github.com/kazupon/vue-i18n/pull/869) fix: not string method access error ([@kazupon](https://github.com/kazupon)) #### :pencil: Documentation * [#867](https://github.com/kazupon/vue-i18n/pull/867) docs: [RU] Translation ([@Alex-Sokolov](https://github.com/Alex-Sokolov)) * [#865](https://github.com/kazupon/vue-i18n/pull/865) improvement(docs): extend Hot reloading section ([@caugner](https://github.com/caugner)) #### Committers: 3 - Alexander Sokolov ([@Alex-Sokolov](https://github.com/Alex-Sokolov)) - Claas Augner ([@caugner](https://github.com/caugner)) - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.17.4 (2020-04-26) #### :bug: Bug Fixes * [#859](https://github.com/kazupon/vue-i18n/pull/859) fix datetime format cache ([@kazupon](https://github.com/kazupon)) * [#858](https://github.com/kazupon/vue-i18n/pull/858) fix datetime and number format fallbacking ([@kazupon](https://github.com/kazupon)) * [#857](https://github.com/kazupon/vue-i18n/pull/857) fix: alternative array includes ([@kazupon](https://github.com/kazupon)) #### Committers: 1 - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.17.3 (2020-04-19) #### :zap: Improved Features * [#846](https://github.com/kazupon/vue-i18n/pull/846) add key to postTranslation ([@dmitryuk](https://github.com/dmitryuk)) #### :pencil: Documentation * [#847](https://github.com/kazupon/vue-i18n/pull/847) docs: Update /api/README.md ([@Alex-Sokolov](https://github.com/Alex-Sokolov)) #### Committers: 2 - Alexander Dmitryuk ([@dmitryuk](https://github.com/dmitryuk)) - Alexander Sokolov ([@Alex-Sokolov](https://github.com/Alex-Sokolov)) ## v8.17.2 (2020-04-18) #### :zap: Improved Features * [#844](https://github.com/kazupon/vue-i18n/pull/844) Use plain object instead of Map, which is not supported in IE9/10 ([@exoego](https://github.com/exoego)) #### Committers: 1 - TATSUNO Yasuhiro ([@exoego](https://github.com/exoego)) ## v8.17.1 (2020-04-16) #### :bug: Bug Fixes * [#840](https://github.com/kazupon/vue-i18n/pull/840) fix: altnative endsWidth ([@kazupon](https://github.com/kazupon)) #### :pencil: Documentation * [#837](https://github.com/kazupon/vue-i18n/pull/837) Fix typo ([@ninofiliu](https://github.com/ninofiliu)) #### Committers: 2 - Nino Filiu ([@ninofiliu](https://github.com/ninofiliu)) - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.17.0 (2020-04-11) #### :star: New Features * [#829](https://github.com/kazupon/vue-i18n/pull/829) #138 Fallback Locale as array for cascading fallbacks ([@mmokross](https://github.com/mmokross)) #### :pencil: Documentation * [#834](https://github.com/kazupon/vue-i18n/pull/834) Add capitalize default modifier in doc ([@alexandreDavid](https://github.com/alexandreDavid)) * [#832](https://github.com/kazupon/vue-i18n/pull/832) fix in examples of "Custom pluralization" ([@Perlover](https://github.com/Perlover)) #### Committers: 4 - Alexandre David ([@alexandreDavid](https://github.com/alexandreDavid)) - Kobayashi Kazuhiro ([@kzhrk](https://github.com/kzhrk)) - Michael Mokroß ([@mmokross](https://github.com/mmokross)) - Perlover ([@Perlover](https://github.com/Perlover)) ## v8.16.0 (2020-03-27) #### :star: New Features * [#822](https://github.com/kazupon/vue-i18n/pull/822) post translation hooking feature ([@kazupon](https://github.com/kazupon)) #### Committers: 1 - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## v8.15.7 (2020-03-25) #### :bug: Bug Fixes * [#819](https://github.com/kazupon/vue-i18n/pull/819) Fixed bug when changing number format runtime ([@slischka](https://github.com/slischka)) #### Committers: 1 - Jiří Slischka ([@slischka](https://github.com/slischka)) ## v8.15.6 (2020-03-23) #### :bug: Bug Fixes * [#817](https://github.com/kazupon/vue-i18n/pull/817) Bugfix : 'setLocaleMessage' / 'mergeLocaleMessage' doesn't work if 'warnHtmlInMessage' is set to 'error' ([@aym3nb](https://github.com/aym3nb)) #### :pencil: Documentation * [#816](https://github.com/kazupon/vue-i18n/pull/816) Update fallback.md ([@scoutrul](https://github.com/scoutrul)) #### Committers: 3 - Anton ([@scoutrul](https://github.com/scoutrul)) - Aymen Bareche ([@aym3nb](https://github.com/aym3nb)) - TATSUNO Yasuhiro ([@exoego](https://github.com/exoego)) ## v8.15.5 (2020-03-07) #### :star: Features * [#787](https://github.com/kazupon/vue-i18n/pull/787) Add a 'capitalize' default modifier for linked message ([@charlesmass](https://github.com/charlesmass)) #### :zap: Improvement Features * [#794](https://github.com/kazupon/vue-i18n/pull/794) Support returning 'string' type for customized interpolation ([@sihyeonn](https://github.com/sihyeonn)) #### :pencil: Documentation * [#791](https://github.com/kazupon/vue-i18n/pull/791) Revise fallback.md ([@jlebar](https://github.com/jlebar)) #### Committers: 4 - Justin Lebar ([@jlebar](https://github.com/jlebar)) - L M ([@charlesmass](https://github.com/charlesmass)) - Sihyeon Jang ([@sihyeonn](https://github.com/sihyeonn)) - kazuya kawaguchi ([@kazupon](https://github.com/kazupon)) ## [8.15.4](https://github.com/kazupon/vue-i18n/compare/v8.15.3...v8.15.4) (2020-02-25) ### :bug: Bug Fixes * **index:** improve formatFallbackMessages code (#779) (#783) by [@masongzhi](https://github.com/masongzhi) ([53895b9](https://github.com/kazupon/vue-i18n/commit/53895b9))) ### :up: Updates * some fixes ([8a9a950](https://github.com/kazupon/vue-i18n/commit/8a9a950)) ## [8.15.3](https://github.com/kazupon/vue-i18n/compare/v8.15.2...v8.15.3) (2019-12-18) ### :zap: Improvements * **index:** fix mergeLocaleMessage. add changes notification on merging with an empty target object ([#752](https://github.com/kazupon/vue-i18n/issues/752)) by [@jekill](https://github.com/jekill) ([048eac5](https://github.com/kazupon/vue-i18n/commit/048eac5)), closes [#752](https://github.com/kazupon/vue-i18n/issues/752) ## [8.15.2](https://github.com/kazupon/vue-i18n/compare/v8.15.1...v8.15.2) (2019-12-18) ### :bug: Bug Fixes * **index:** Fix exception when using unit number formatting by [@simonjodet](https://github.com/simonjodet) ([194b801](https://github.com/kazupon/vue-i18n/commit/194b801)), closes [#750](https://github.com/kazupon/vue-i18n/issues/750) [#751](https://github.com/kazupon/vue-i18n/issues/751) ## [8.15.1](https://github.com/kazupon/vue-i18n/compare/v8.15.0...v8.15.1) (2019-11-27) ### :zap: Improvements * **mixin:** change to custom blocks parse error ([a9858be](https://github.com/kazupon/vue-i18n/commit/a9858be)) # [8.15.0](https://github.com/kazupon/vue-i18n/compare/v8.14.1...v8.15.0) (2019-10-16) ### :star: New Features * Add constructor option for custom modifiers ([#724](https://github.com/kazupon/vue-i18n/issues/724)) by [@epaezrubio](https://github.com/epaezrubio) ([3217212](https://github.com/kazupon/vue-i18n/commit/3217212)), closes [#724](https://github.com/kazupon/vue-i18n/issues/724) ## [8.14.1](https://github.com/kazupon/vue-i18n/compare/v8.14.0...v8.14.1) (2019-09-12) ### :bug: Bug Fixes * **path:** fix branket key error ([8d2aba7](https://github.com/kazupon/vue-i18n/commit/8d2aba7)) * **component:** Fix interpolation component when there are empty text nodes ([547cdd1](https://github.com/kazupon/vue-i18n/commit/547cdd1)) by [@Demivan](https://github.com/Demivan) # [8.14.0](https://github.com/kazupon/vue-i18n/compare/v8.13.0...v8.14.0) (2019-08-12) ### :star: New Features * fallback formatting ([#637](https://github.com/kazupon/vue-i18n/issues/637)) by [@sebwas](https://github.com/sebwas) ([bf9929c](https://github.com/kazupon/vue-i18n/commit/bf9929c)), closes [#637](https://github.com/kazupon/vue-i18n/issues/637) * support slots syntax for component interpolation ([#685](https://github.com/kazupon/vue-i18n/issues/685)) by [@aavondet](https://github.com/aavondet) ([71ca843](https://github.com/kazupon/vue-i18n/commit/71ca843)), closes [#685](https://github.com/kazupon/vue-i18n/issues/685) # [8.13.0](https://github.com/kazupon/vue-i18n/compare/v8.12.0...v8.13.0) (2019-08-09) ### :star: New Features * datetime/number formats fallback warning filter ([46de19e](https://github.com/kazupon/vue-i18n/commit/46de19e)), closes [#558](https://github.com/kazupon/vue-i18n/issues/558) * fallback translation warning filter ([69fc798](https://github.com/kazupon/vue-i18n/commit/69fc798)) * translation missing warning filter ([666dc9d](https://github.com/kazupon/vue-i18n/commit/666dc9d)) # [8.12.0](https://github.com/kazupon/vue-i18n/compare/v8.11.2...v8.12.0) (2019-07-09) ### :star: New Features * **mixin:** shared locale messages feature ([82543de](https://github.com/kazupon/vue-i18n/commit/82543de)) ### :zap: Improvements * **typing:** sharedMessages option type ([6967a15](https://github.com/kazupon/vue-i18n/commit/6967a15)) ## [8.11.2](https://github.com/kazupon/vue-i18n/compare/v8.11.1...v8.11.2) (2019-04-30) ### :bug: Bug Fixes * bug(mixin): fix SSR memory leak by moving subscribeDataChanging calls into beforeMount ([#572](https://github.com/kazupon/vue-i18n/issues/572)) by [@Pindar](https://github.com/Pindar) ([32b5795](https://github.com/kazupon/vue-i18n/commit/32b5795)), closes [#572](https://github.com/kazupon/vue-i18n/issues/572) ## [8.11.1](https://github.com/kazupon/vue-i18n/compare/v8.11.0...v8.11.1) (2019-04-26) ### :bug: Bug Fixes * fix ES Modules distribution ([bb631a1](https://github.com/kazupon/vue-i18n/commit/bb631a1)) # [8.11.0](https://github.com/kazupon/vue-i18n/compare/v8.10.0...v8.11.0) (2019-04-26) ### :star: New Features * ES modules for browser ([#561](https://github.com/kazupon/vue-i18n/issues/561)) ([c9b9adf](https://github.com/kazupon/vue-i18n/commit/c9b9adf)), closes [#561](https://github.com/kazupon/vue-i18n/issues/561) * HTML locale message warning option ([#567](https://github.com/kazupon/vue-i18n/issues/567)) ([4aecf03](https://github.com/kazupon/vue-i18n/commit/4aecf03)), closes [#567](https://github.com/kazupon/vue-i18n/issues/567) # [8.10.0](https://github.com/kazupon/vue-i18n/compare/v8.9.0...v8.10.0) (2019-03-28) ### :star: New Features * **number:** i18n-n functional component ([#541](https://github.com/kazupon/vue-i18n/issues/541)) by [@bponomarenko](https://github.com/bponomarenko) ([b33579d](https://github.com/kazupon/vue-i18n/commit/b33579d)), closes [#541](https://github.com/kazupon/vue-i18n/issues/541) * **path:** Keypath should parse if sub path contains spaces. ([#533](https://github.com/kazupon/vue-i18n/issues/533)) by [@exoego](https://github.com/exoego) ([640daaf](https://github.com/kazupon/vue-i18n/commit/640daaf)), closes [#533](https://github.com/kazupon/vue-i18n/issues/533) ### :zap: Improvements * **number:** support data fall through in i18n-n ([#545](https://github.com/kazupon/vue-i18n/issues/545)) ([71cadbf](https://github.com/kazupon/vue-i18n/commit/71cadbf)), closes [#545](https://github.com/kazupon/vue-i18n/issues/545) ### :pencil: docs * **vuepress:** translate documents for chinese ([#536](https://github.com/kazupon/vue-i18n/issues/536)) by [@xuhongbo](https://github.com/xuhongbo) ([ccf29f8](https://github.com/kazupon/vue-i18n/commit/ccf29f8)), closes [#536](https://github.com/kazupon/vue-i18n/issues/536) [#531](https://github.com/kazupon/vue-i18n/issues/531) [#1](https://github.com/kazupon/vue-i18n/issues/1) [#533](https://github.com/kazupon/vue-i18n/issues/533) [#540](https://github.com/kazupon/vue-i18n/issues/540) [#541](https://github.com/kazupon/vue-i18n/issues/541) [#1](https://github.com/kazupon/vue-i18n/issues/1) [#2](https://github.com/kazupon/vue-i18n/issues/2) # [8.9.0](https://github.com/kazupon/vue-i18n/compare/v8.8.2...v8.9.0) (2019-03-08) ### :bug: Bug Fixes * **index:** Fix [#515](https://github.com/kazupon/vue-i18n/issues/515) empty string not returning true ([#525](https://github.com/kazupon/vue-i18n/issues/525)) by [@kimuraz](https://github.com/kimuraz) ([396c5ca](https://github.com/kazupon/vue-i18n/commit/396c5ca)), closes [#515](https://github.com/kazupon/vue-i18n/issues/515) [#525](https://github.com/kazupon/vue-i18n/issues/525) [#515](https://github.com/kazupon/vue-i18n/issues/515) ### :star: New Features * **index:** add availableLocales (related issue [#193](https://github.com/kazupon/vue-i18n/issues/193), PR [#528](https://github.com/kazupon/vue-i18n/issues/528)) by [@exoego](https://github.com/exoego) ([8f75b1f](https://github.com/kazupon/vue-i18n/commit/8f75b1f)), closes [#193](https://github.com/kazupon/vue-i18n/issues/193) [#528](https://github.com/kazupon/vue-i18n/issues/528) [#193](https://github.com/kazupon/vue-i18n/issues/193) [#193](https://github.com/kazupon/vue-i18n/issues/193) [#193](https://github.com/kazupon/vue-i18n/issues/193) [#193](https://github.com/kazupon/vue-i18n/issues/193) ### :zap: Improvements * **flowtype:** Fix missing type declarations in flow type ([#529](https://github.com/kazupon/vue-i18n/issues/529)) by [@exoego](https://github.com/exoego) ([4173764](https://github.com/kazupon/vue-i18n/commit/4173764)), closes [#529](https://github.com/kazupon/vue-i18n/issues/529) ## [8.8.2](https://github.com/kazupon/vue-i18n/compare/v8.8.1...v8.8.2) (2019-02-17) ### :bug: Bug Fixes * **mixin:** fix memory leak ([135058d](https://github.com/kazupon/vue-i18n/commit/135058d)), closes [#514](https://github.com/kazupon/vue-i18n/issues/514) ## [8.8.1](https://github.com/kazupon/vue-i18n/compare/v8.8.0...v8.8.1) (2019-02-10) ### :bug: Bug Fixes * **index:** fixed [#478](https://github.com/kazupon/vue-i18n/issues/478) ([#518](https://github.com/kazupon/vue-i18n/issues/518)) by [@stroncium](https://github.com/stroncium) ([469edd9](https://github.com/kazupon/vue-i18n/commit/469edd9)), closes [#478](https://github.com/kazupon/vue-i18n/issues/478) [#518](https://github.com/kazupon/vue-i18n/issues/518) [#478](https://github.com/kazupon/vue-i18n/issues/478) ### :zap: Improvements * **flowtype:** update typings ([44e04e7](https://github.com/kazupon/vue-i18n/commit/44e04e7)) * **typescript:** update typings ([dee35b9](https://github.com/kazupon/vue-i18n/commit/dee35b9)) # [8.8.0](https://github.com/kazupon/vue-i18n/compare/v8.7.0...v8.8.0) (2019-01-29) ### :bug: Bug Fixes * **index:** fix flat path based key issue ([bed9c39](https://github.com/kazupon/vue-i18n/commit/bed9c39)), closes [#349](https://github.com/kazupon/vue-i18n/issues/349) * **mixin:** fix beforeDestroy can not find this.$t ([#500](https://github.com/kazupon/vue-i18n/issues/500)) by [@masongzhi](https://github.com/masongzhi) ([311b8f3](https://github.com/kazupon/vue-i18n/commit/311b8f3)), closes [#500](https://github.com/kazupon/vue-i18n/issues/500) ### :zap: Improvements * **directive:** Fix typo on warning message ([#509](https://github.com/kazupon/vue-i18n/issues/509)) by [@kimuraz](https://github.com/kimuraz) ([e879024](https://github.com/kazupon/vue-i18n/commit/e879024)), closes [#509](https://github.com/kazupon/vue-i18n/issues/509) * **index:** silence fallback warnings ([#510](https://github.com/kazupon/vue-i18n/issues/510)) by [@SzNagyMisu](https://github.com/SzNagyMisu) ([ddc0c79](https://github.com/kazupon/vue-i18n/commit/ddc0c79)), closes [#510](https://github.com/kazupon/vue-i18n/issues/510) [#139](https://github.com/kazupon/vue-i18n/issues/139) # [8.7.0](https://github.com/kazupon/vue-i18n/compare/v8.6.0...v8.7.0) (2019-01-02) ### :zap: Improvements * **directive:** Preserve directive content ([#495](https://github.com/kazupon/vue-i18n/issues/495)) by [@bponomarenko](https://github.com/bponomarenko) ([c29edba](https://github.com/kazupon/vue-i18n/commit/c29edba)), closes [#495](https://github.com/kazupon/vue-i18n/issues/495) [#408](https://github.com/kazupon/vue-i18n/issues/408) [#408](https://github.com/kazupon/vue-i18n/issues/408) # [8.6.0](https://github.com/kazupon/vue-i18n/compare/v8.5.0...v8.6.0) (2018-12-25) ### :bug: Bug Fixes * **pluralization:** inherit pluralization rules ⚠ ([#493](https://github.com/kazupon/vue-i18n/issues/493)) by [@Raiondesu](https://github.com/Raiondesu) ([7a23f32](https://github.com/kazupon/vue-i18n/commit/7a23f32)), closes [#493](https://github.com/kazupon/vue-i18n/issues/493) ### :zap: Improvements * **format:** Add the path as argument to the custom formatter ([#489](https://github.com/kazupon/vue-i18n/issues/489)) by [@Raiondesu](https://github.com/Raiondesu) ([b9437ea](https://github.com/kazupon/vue-i18n/commit/b9437ea)), closes [#489](https://github.com/kazupon/vue-i18n/issues/489) [#484](https://github.com/kazupon/vue-i18n/issues/484) [#484](https://github.com/kazupon/vue-i18n/issues/484) # [8.5.0](https://github.com/kazupon/vue-i18n/compare/v8.4.0...v8.5.0) (2018-12-17) ### :bug: Bug Fixes * **index:** evaluate availabilities lazily (fix [#477](https://github.com/kazupon/vue-i18n/issues/477)) ([#483](https://github.com/kazupon/vue-i18n/issues/483)) by [@gamtiq](https://github.com/gamtiq) ([b66f02e](https://github.com/kazupon/vue-i18n/commit/b66f02e)), closes [#477](https://github.com/kazupon/vue-i18n/issues/477) [#483](https://github.com/kazupon/vue-i18n/issues/483) ### :zap: Improvements * **index:** Allow pluralization customization via constructor options (closes [#464](https://github.com/kazupon/vue-i18n/issues/464)) ([#482](https://github.com/kazupon/vue-i18n/issues/482)) by [@Raiondesu](https://github.com/Raiondesu) ([ef4b1a6](https://github.com/kazupon/vue-i18n/commit/ef4b1a6)), closes [#464](https://github.com/kazupon/vue-i18n/issues/464) [#482](https://github.com/kazupon/vue-i18n/issues/482) [#464](https://github.com/kazupon/vue-i18n/issues/464) [#464](https://github.com/kazupon/vue-i18n/issues/464) [#464](https://github.com/kazupon/vue-i18n/issues/464) [#464](https://github.com/kazupon/vue-i18n/issues/464) [#451](https://github.com/kazupon/vue-i18n/issues/451) * **index:** make silentTranslationWarn work for dates and numbers too ([#481](https://github.com/kazupon/vue-i18n/issues/481)) by [@Raiondesu](https://github.com/Raiondesu) ([402092b](https://github.com/kazupon/vue-i18n/commit/402092b)), closes [#481](https://github.com/kazupon/vue-i18n/issues/481) * **types:** typed autocomplete in date and number format options ([#485](https://github.com/kazupon/vue-i18n/issues/485)) by [@Raiondesu](https://github.com/Raiondesu) ([e2e5993](https://github.com/kazupon/vue-i18n/commit/e2e5993)), closes [#485](https://github.com/kazupon/vue-i18n/issues/485) # [8.4.0](https://github.com/kazupon/vue-i18n/compare/v8.3.2...v8.4.0) (2018-11-30) ### :star: New Features * **index:** Add linked message formatting ([#467](https://github.com/kazupon/vue-i18n/issues/467)) by [@exoego](https://github.com/exoego) ([776b81b](https://github.com/kazupon/vue-i18n/commit/776b81b)), closes [#467](https://github.com/kazupon/vue-i18n/issues/467) ## [8.3.2](https://github.com/kazupon/vue-i18n/compare/v8.3.1...v8.3.2) (2018-11-16) ### :chart_with_upwards_trend: Performance Fixes * **index:** Optimize unnecessary capturing. ([#462](https://github.com/kazupon/vue-i18n/issues/462)) by [@exoego](https://github.com/exoego) ([116845e](https://github.com/kazupon/vue-i18n/commit/116845e)), closes [#462](https://github.com/kazupon/vue-i18n/issues/462) ## [8.3.1](https://github.com/kazupon/vue-i18n/compare/v8.3.0...v8.3.1) (2018-11-08) ### :bug: Bug Fixes * **directive:** fix cannnot update with v-t when had been changed locale message ([4895a2e](https://github.com/kazupon/vue-i18n/commit/4895a2e)), closes [#450](https://github.com/kazupon/vue-i18n/issues/450) * **index:** fix merge bug ([1798490](https://github.com/kazupon/vue-i18n/commit/1798490)), closes [#458](https://github.com/kazupon/vue-i18n/issues/458) * **missing:** fix vm argument passing ([dc48099](https://github.com/kazupon/vue-i18n/commit/dc48099)), closes [#453](https://github.com/kazupon/vue-i18n/issues/453) ### :zap: Improvements * Optimize path.js and format.js ([#456](https://github.com/kazupon/vue-i18n/issues/456)) by [@exoego](https://github.com/exoego) ([639453c](https://github.com/kazupon/vue-i18n/commit/639453c)), closes [#456](https://github.com/kazupon/vue-i18n/issues/456) # [8.3.0](https://github.com/kazupon/vue-i18n/compare/v8.2.1...v8.3.0) (2018-10-29) ### :zap: Improvements * **pluralization:** Extendable pluralization by [@Raiondesu](https://github.com/Raiondesu) ([bbab90b](https://github.com/kazupon/vue-i18n/commit/bbab90b)) ## [8.2.1](https://github.com/kazupon/vue-i18n/compare/v8.2.0...v8.2.1) (2018-10-15) ### :bug: Bug Fixes * **extend:** fix TypeError: Cannot redefine property: $i18n ([#422](https://github.com/kazupon/vue-i18n/issues/422)) by [@HadiChen](https://github.com/HadiChen) ([cb19082](https://github.com/kazupon/vue-i18n/commit/cb19082)), closes [#422](https://github.com/kazupon/vue-i18n/issues/422) ### :zap: Improvements * **index:** Suppress some warnings in production: smaller min.js and performance gain. ([#441](https://github.com/kazupon/vue-i18n/issues/441)) by @ exoego ([43931f5](https://github.com/kazupon/vue-i18n/commit/43931f5)), closes [#441](https://github.com/kazupon/vue-i18n/issues/441) # [8.2.0](https://github.com/kazupon/vue-i18n/compare/v8.1.1...v8.2.0) (2018-10-13) ### :bug: Bug Fixes * **index:** Add warning for circular reference in linked message ([#438](https://github.com/kazupon/vue-i18n/issues/438)) by [@exoego](https://github.com/exoego) ([7583485](https://github.com/kazupon/vue-i18n/commit/7583485)), closes [#438](https://github.com/kazupon/vue-i18n/issues/438) ### :zap: Improvements * **index:** Allow escaping link key like @:(foo.bar). ([#437](https://github.com/kazupon/vue-i18n/issues/437)) by [@exoego](https://github.com/exoego) ([acfc458](https://github.com/kazupon/vue-i18n/commit/acfc458)), closes [#437](https://github.com/kazupon/vue-i18n/issues/437) * **index:** Pre-defined named arguments for Pluraization ([#440](https://github.com/kazupon/vue-i18n/issues/440)) by [@exoego](https://github.com/exoego) ([e84f0fb](https://github.com/kazupon/vue-i18n/commit/e84f0fb)), closes [#440](https://github.com/kazupon/vue-i18n/issues/440) * **path:** Allow non-ascii chars including numbers. ([#436](https://github.com/kazupon/vue-i18n/issues/436)) by [@exoego](https://github.com/exoego) ([a556c58](https://github.com/kazupon/vue-i18n/commit/a556c58)), closes [#436](https://github.com/kazupon/vue-i18n/issues/436) ## [8.1.1](https://github.com/kazupon/vue-i18n/compare/v8.1.0...v8.1.1) (2018-10-12) ### :bug: Bug Fixes * **build:** fix rollup building issues ([1a1958a](https://github.com/kazupon/vue-i18n/commit/1a1958a)) * **format:** Should warn as unknown if named format is not closed. ([#435](https://github.com/kazupon/vue-i18n/issues/435)) by [@exoego](https://github.com/exoego) ([d1f6ed0](https://github.com/kazupon/vue-i18n/commit/d1f6ed0)), closes [#435](https://github.com/kazupon/vue-i18n/issues/435) * **install:** fix cannot redfine error ([6d5ec61](https://github.com/kazupon/vue-i18n/commit/6d5ec61)) ### :zap: Improvements * **package.json:** tree shaking optimization ([38948c5](https://github.com/kazupon/vue-i18n/commit/38948c5)) # [8.1.0](https://github.com/kazupon/vue-i18n/compare/v8.0.0...v8.1.0) (2018-09-03) ### :bug: Bug Fixes * **install:** add support for Vue.extend vue-i18n instance ([#420](https://github.com/kazupon/vue-i18n/issues/420)) by [@jaredzhu1993](https://github.com/jaredzhu1993) ([a60ea8b](https://github.com/kazupon/vue-i18n/commit/a60ea8b)), closes [#420](https://github.com/kazupon/vue-i18n/issues/420) ### :zap: Improvements * **warnings:** make warning messages clearer ([#396](https://github.com/kazupon/vue-i18n/issues/396)) by [@kimuraz](https://github.com/kimuraz) ([79eee1b](https://github.com/kazupon/vue-i18n/commit/79eee1b)), closes [#396](https://github.com/kazupon/vue-i18n/issues/396) # [8.0.0](https://github.com/kazupon/vue-i18n/compare/v7.8.1...v8.0.0) (2018-06-23) ### :boom: Breaking changes * **extend:** fix this context binding ([aa0e831](https://github.com/kazupon/vue-i18n/commit/aa0e831)), closes [#306](https://github.com/kazupon/vue-i18n/issues/306) [#286](https://github.com/kazupon/vue-i18n/issues/286) [#259](https://github.com/kazupon/vue-i18n/issues/259), revert [#260](https://github.com/kazupon/vue-i18n/issues/260) Note that you need to guarantee this context equal to component instance in lifecycle methods (e.g. in `data` options, `const $t = this.$t.bind(this)`). ```js export default { data () { const $t = this.$t.bind(this) return { msg: $t('msg') } } } ``` see the [API docs](https://kazupon.github.io/vue-i18n/api/) ### :bug: Bug Fixes * bug(directive): fix guard checking at unbind ([c74888c](https://github.com/kazupon/vue-i18n/commit/c74888c)), closes [#340](https://github.com/kazupon/vue-i18n/issues/340) ### NOTE * extend: ## [7.8.1](https://github.com/kazupon/vue-i18n/compare/v7.8.0...v7.8.1) (2018-06-18) ### :bug: Bug Fixes * **directive:** fix cannot unbind bug ([105888d](https://github.com/kazupon/vue-i18n/commit/105888d)), closes [#377](https://github.com/kazupon/vue-i18n/issues/377) # [7.8.0](https://github.com/kazupon/vue-i18n/compare/v7.7.0...v7.8.0) (2018-06-01) ### :zap: Improvements * **typescript:** add type exportings ([a7cb8da](https://github.com/kazupon/vue-i18n/commit/a7cb8da)) # [7.7.0](https://github.com/kazupon/vue-i18n/compare/v7.6.0...v7.7.0) (2018-05-20) ### :zap: Improvements * **index:** resource reactivity ([887a137](https://github.com/kazupon/vue-i18n/commit/887a137)), closes [#253](https://github.com/kazupon/vue-i18n/issues/253) * **typescript:** Fix typings in components ([#344](https://github.com/kazupon/vue-i18n/issues/344)) by [@Demivan](https://github.com/Demivan) ([2402893](https://github.com/kazupon/vue-i18n/commit/2402893)), closes [#344](https://github.com/kazupon/vue-i18n/issues/344) # [7.6.0](https://github.com/kazupon/vue-i18n/compare/v7.5.0...v7.6.0) (2018-03-13) ### :zap: Improvements * **index:** support retunable missing handler ([#256](https://github.com/kazupon/vue-i18n/issues/256)) by [@houd1ni](https://github.com/houd1ni) ([9fbe467](https://github.com/kazupon/vue-i18n/commit/9fbe467)) * **typescript:** update TranslateResult type interface ([dffc678](https://github.com/kazupon/vue-i18n/commit/dffc678)) # [7.5.0](https://github.com/kazupon/vue-i18n/compare/v7.4.2...v7.5.0) (2018-03-11) ### :star: New Features * **directive:** Add pluralization feature to directive ([#304](https://github.com/kazupon/vue-i18n/issues/304)) by [@SirLamer](https://github.com/SirLamer) ([8378859](https://github.com/kazupon/vue-i18n/commit/8378859)) ### :zap: Improvements * **flow:** update TranslateResult type interface ([59f4658](https://github.com/kazupon/vue-i18n/commit/59f4658)) * **index:** support object localization ([#311](https://github.com/kazupon/vue-i18n/issues/311)) by [@manniL](https://github.com/manniL) ([99e5006](https://github.com/kazupon/vue-i18n/commit/99e5006)) * **missing:** Add interpolation values to missing handler ([#308](https://github.com/kazupon/vue-i18n/issues/308)) by [@sebwas](https://github.com/sebwas) ([b912d8a](https://github.com/kazupon/vue-i18n/commit/b912d8a)) * **numberformat:** Explicit number format options ([#305](https://github.com/kazupon/vue-i18n/issues/305)) by [@bponomarenko](https://github.com/bponomarenko) ([aa07450](https://github.com/kazupon/vue-i18n/commit/aa07450)) ## [7.4.2](https://github.com/kazupon/vue-i18n/compare/v7.4.1...v7.4.2) (2018-02-01) ### :zap: Improvements * **index:** Fixes global auto installation ([#291](https://github.com/kazupon/vue-i18n/issues/291)) by [@emileber](https://github.com/emileber) ([2f016ff](https://github.com/kazupon/vue-i18n/commit/2f016ff)), closes [#291](https://github.com/kazupon/vue-i18n/issues/291) ## [7.4.1](https://github.com/kazupon/vue-i18n/compare/v7.4.0...v7.4.1) (2018-01-25) ### :bug: Bug Fixes * fix cannot react ([2a8ea1c](https://github.com/kazupon/vue-i18n/commit/2a8ea1c)), closes [#261](https://github.com/kazupon/vue-i18n/issues/261) ### :zap: Improvements * **formatter:** interpolate messages without values ([#282](https://github.com/kazupon/vue-i18n/issues/282)) by [@cb8](https://github.com/cb8) ([b792ce2](https://github.com/kazupon/vue-i18n/commit/b792ce2)) # [7.4.0](https://github.com/kazupon/vue-i18n/compare/v7.3.4...v7.4.0) (2018-01-10) ### :star: New Features * **typescript:** Allow module augmentation ([#273](https://github.com/kazupon/vue-i18n/issues/273)) by [@CKGrafico](https://github.com/CKGrafico) ([4371344](https://github.com/kazupon/vue-i18n/commit/4371344)) ## [7.3.4](https://github.com/kazupon/vue-i18n/compare/v7.3.3...v7.3.4) (2018-01-07) ### :bug: Bug Fixes * **formatter:** Inherit formatter ([#269](https://github.com/kazupon/vue-i18n/issues/269)) by [@podkot](https://github.com/podkot) ([26a33ad](https://github.com/kazupon/vue-i18n/commit/26a33ad)) ## [7.3.3](https://github.com/kazupon/vue-i18n/compare/v7.3.2...v7.3.3) (2017-12-19) ### :bug: Bug Fixes * **extend:** Fix this not found [#259](https://github.com/kazupon/vue-i18n/issues/259) ([#260](https://github.com/kazupon/vue-i18n/issues/260)) by [@lzxb](https://github.com/lzxb) ([c29007e](https://github.com/kazupon/vue-i18n/commit/c29007e)), closes [#259](https://github.com/kazupon/vue-i18n/issues/259) [#260](https://github.com/kazupon/vue-i18n/issues/260) * **types:** fix using old export ([#263](https://github.com/kazupon/vue-i18n/issues/263)) by [@jmigual](https://github.com/jmigual) ([b295fee](https://github.com/kazupon/vue-i18n/commit/b295fee)), closes [#263](https://github.com/kazupon/vue-i18n/issues/263) ## [7.3.2](https://github.com/kazupon/vue-i18n/compare/v7.3.1...v7.3.2) (2017-10-19) ### :zap: Improvements * **typescript:** fix import problem of vue2.5 because of the types update ([#238](https://github.com/kazupon/vue-i18n/issues/238)) by [@peterchealse](https://github.com/peterchealse) ([cb98347](https://github.com/kazupon/vue-i18n/commit/cb98347)), closes [#238](https://github.com/kazupon/vue-i18n/issues/238) ## [7.3.1](https://github.com/kazupon/vue-i18n/compare/v7.3.0...v7.3.1) (2017-10-04) ### :bug: Bug Fixes * **directive:** fix cannot locale reactivity ([e1fc12e](https://github.com/kazupon/vue-i18n/commit/e1fc12e)), closes [#227](https://github.com/kazupon/vue-i18n/issues/227) # [7.3.0](https://github.com/kazupon/vue-i18n/compare/v7.2.0...v7.3.0) (2017-09-22) ### :star: New Features * **directives:** support v-t custom directive (welcome back!) ([af9a2e7](https://github.com/kazupon/vue-i18n/commit/af9a2e7)) ### :up: Updates * **typing:** fix flowtype ([fa06f44](https://github.com/kazupon/vue-i18n/commit/fa06f44)) # [7.2.0](https://github.com/kazupon/vue-i18n/compare/v7.1.2...v7.2.0) (2017-08-28) ### :star: New Features * **interpolation:** list formatting refactor and places/place feature ([#218](https://github.com/kazupon/vue-i18n/issues/218)) by [@myst729](https://github.com/myst729) ([0f0f3ff](https://github.com/kazupon/vue-i18n/commit/0f0f3ff)) ## [7.1.2](https://github.com/kazupon/vue-i18n/compare/v7.1.1...v7.1.2) (2017-08-25) ### :zap: Improvements * **interpolation:** skip non-element VNode in interpolation ([#211](https://github.com/kazupon/vue-i18n/issues/211)) by [@myst729](https://github.com/myst729) ([6be1756](https://github.com/kazupon/vue-i18n/commit/6be1756)) ## [7.1.1](https://github.com/kazupon/vue-i18n/compare/v7.1.0...v7.1.1) (2017-08-03) ### :bug: Bug Fixes * **mixin:** fix cannot setup VueI18n instance ([13585a4](https://github.com/kazupon/vue-i18n/commit/13585a4)), closes [#203](https://github.com/kazupon/vue-i18n/issues/203) # [7.1.0](https://github.com/kazupon/vue-i18n/compare/v7.0.5...v7.1.0) (2017-07-30) ### :zap: Improvements * **custom-block:** support multiple custom blocks ([ab955a5](https://github.com/kazupon/vue-i18n/commit/ab955a5)), closes [#189](https://github.com/kazupon/vue-i18n/issues/189) ## [7.0.5](https://github.com/kazupon/vue-i18n/compare/v7.0.4...v7.0.5) (2017-07-08) ### :bug: Bug Fixes * **format:** fix cannot collectly parse percent ([fc71eda](https://github.com/kazupon/vue-i18n/commit/fc71eda)), closes [#191](https://github.com/kazupon/vue-i18n/issues/191) ## [7.0.4](https://github.com/kazupon/vue-i18n/compare/v7.0.3...v7.0.4) (2017-07-01) ### :bug: Bug Fixes * **link:** fix ie traverse custom Array.prototype method ([#188](https://github.com/kazupon/vue-i18n/issues/188)) by [@632781460](https://github.com/632781460) ([d3b308b](https://github.com/kazupon/vue-i18n/commit/d3b308b)), closes [#188](https://github.com/kazupon/vue-i18n/issues/188) ### :chart_with_upwards_trend: Performance Fixes * fix blocking at beforeDestroy ([570b215](https://github.com/kazupon/vue-i18n/commit/570b215)), closes [#187](https://github.com/kazupon/vue-i18n/issues/187) ## [7.0.3](https://github.com/kazupon/vue-i18n/compare/v7.0.2...v7.0.3) (2017-06-13) ### :bug: Bug Fixes * **fallback:** fix cannot fallabck localization ([694e6f2](https://github.com/kazupon/vue-i18n/commit/694e6f2)), closes [#176](https://github.com/kazupon/vue-i18n/issues/176) * **fallback:** fix fallback locale issue ([d9ceddc](https://github.com/kazupon/vue-i18n/commit/d9ceddc)), closes [#174](https://github.com/kazupon/vue-i18n/issues/174) * **linked:** fix cannot fallback linked localization ([0c572f3](https://github.com/kazupon/vue-i18n/commit/0c572f3)), closes [#172](https://github.com/kazupon/vue-i18n/issues/172) ## [7.0.2](https://github.com/kazupon/vue-i18n/compare/v7.0.1...v7.0.2) (2017-06-10) ### :bug: Bug Fixes * **sfc:** fix cannot parse custom block locale messages ([32eb3a7](https://github.com/kazupon/vue-i18n/commit/32eb3a7)), closes [#173](https://github.com/kazupon/vue-i18n/issues/173) ## [7.0.1](https://github.com/kazupon/vue-i18n/compare/v7.0.0...v7.0.1) (2017-06-04) ### :bug: Bug Fixes * fix cannat single file component translation ([687d406](https://github.com/kazupon/vue-i18n/commit/687d406)), closes [#169](https://github.com/kazupon/vue-i18n/issues/169) * fix cannnot resolve linked localization with component interpolation ([c973619](https://github.com/kazupon/vue-i18n/commit/c973619)), closes [#171](https://github.com/kazupon/vue-i18n/issues/171) * fix datetime and number fallback localization ([be9e1bd](https://github.com/kazupon/vue-i18n/commit/be9e1bd)), closes [#168](https://github.com/kazupon/vue-i18n/issues/168) * fix linked translation with using hyphen or underscore keypath ([6e9f151](https://github.com/kazupon/vue-i18n/commit/6e9f151)), closes [#170](https://github.com/kazupon/vue-i18n/issues/170) # [7.0.0](https://github.com/kazupon/vue-i18n/compare/v7.0.0-rc.1...v7.0.0) (2017-05-29) :tada: :tada: :tada: See the [docs](https://kazupon.github.io/vue-i18n/en/) ### :star: New Features * **datetime localization:** * [documentation](https://github.com/kazupon/vue-i18n/blob/dev/gitbook/en/datetime.md) * [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/datetime) * **number localization:** * [documentation](https://github.com/kazupon/vue-i18n/blob/dev/gitbook/en/number.md) * [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/number) * **component interpolation:** * [documentation](https://github.com/kazupon/vue-i18n/blob/dev/gitbook/en/interpolation.md) * [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/interpolation) * **typescript:** * [type definitions](https://github.com/kazupon/vue-i18n/blob/dev/types/index.d.ts) ### :chart_with_upwards_trend: Performance Fixes * fix translation performance issue ([6032a51](https://github.com/kazupon/vue-i18n/commit/6032a51)) ### :zap: Improvements * **path:** tweak for ssr ### :boom: Breaking changes * **format:** re-impelement formatter * **formatter:** change method nam * **flowtype:** fix locale message related type changing and remove underscore type ### :bug: Bug Fixes * **examples:** fix ssr demo ([059034f](https://github.com/kazupon/vue-i18n/commit/059034f)) * **pluralization:** fix default choice ([240cfed](https://github.com/kazupon/vue-i18n/commit/240cfed)) # [7.0.0-rc.1](https://github.com/kazupon/vue-i18n/compare/v7.0.0-beta.4...v7.0.0-rc.1) (2017-05-26) ### :chart_with_upwards_trend: Performance Fixes * fix translation performance issue ([6032a51](https://github.com/kazupon/vue-i18n/commit/6032a51)), closes [#165](https://github.com/kazupon/vue-i18n/issues/165) ### :up: Updates * **flowtype:** remove unneccesary type ([eb60156](https://github.com/kazupon/vue-i18n/commit/eb60156)) # [7.0.0-beta.4](https://github.com/kazupon/vue-i18n/compare/v7.0.0-beta.3...v7.0.0-beta.4) (2017-05-23) ### :bug: Bug Fixes * **pluralization:** fix default choice ([240cfed](https://github.com/kazupon/vue-i18n/commit/240cfed)), closes [#164](https://github.com/kazupon/vue-i18n/issues/164) # [7.0.0-beta.3](https://github.com/kazupon/vue-i18n/compare/v7.0.0-beta.2...v7.0.0-beta.3) (2017-05-15) ### :up: Updates * bring back from bug fix ([95be4ea](https://github.com/kazupon/vue-i18n/commit/95be4ea)) # [7.0.0-beta.2](https://github.com/kazupon/vue-i18n/compare/v7.0.0-beta.1...v7.0.0-beta.2) (2017-05-14) ### :zap: Improvements * **path:** tweak for ssr ([eb21921](https://github.com/kazupon/vue-i18n/commit/eb21921)) * **typescript:** change custom formatter method name ([c5f043f](https://github.com/kazupon/vue-i18n/commit/c5f043f)) # [7.0.0-beta.1](https://github.com/kazupon/vue-i18n/compare/v6.1.1...v7.0.0-beta.1) (2017-05-11) ### :star: New Features * **datetime localization:** add datetime localization ([3282075](https://github.com/kazupon/vue-i18n/commit/3282075)) * [documentation](https://github.com/kazupon/vue-i18n/blob/dev/gitbook/en/datetime.md) * [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/datetime) * **number localization:** add number localization ([87ee7b3](https://github.com/kazupon/vue-i18n/commit/87ee7b3)) * [documentation](https://github.com/kazupon/vue-i18n/blob/dev/gitbook/en/number.md) * [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/number) * **component interpolation:** ([23f7d34](https://github.com/kazupon/vue-i18n/commit/23f7d34)), closes [#145](https://github.com/kazupon/vue-i18n/issues/145) [#144](https://github.com/kazupon/vue-i18n/issues/144) [#37](https://github.com/kazupon/vue-i18n/issues/37) * [documentation](https://github.com/kazupon/vue-i18n/blob/dev/gitbook/en/interpolation.md) * [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/interpolation) * **typescript:** add TypeScript type definitions ([#161](https://github.com/kazupon/vue-i18n/issues/161)) by [@aicest](https://github.com/aicest) ([61cebca](https://github.com/kazupon/vue-i18n/commit/61cebca)) * [type definitions](https://github.com/kazupon/vue-i18n/blob/dev/types/index.d.ts) ### :boom: Breaking changes * **format:** re-impelement formatter ([a8c046d](https://github.com/kazupon/vue-i18n/commit/a8c046d)) * **formatter:** change method name ([6eed51c](https://github.com/kazupon/vue-i18n/commit/6eed51c)) * **flowtype:** fix locale message related type changing ([c30d576](https://github.com/kazupon/vue-i18n/commit/c30d576)) ### :bug: Bug Fixes * **examples:** fix ssr demo ([059034f](https://github.com/kazupon/vue-i18n/commit/059034f)), closes [#151](https://github.com/kazupon/vue-i18n/issues/151) ## [6.1.3](https://github.com/kazupon/vue-i18n/compare/v6.1.1...v6.1.3) (2017-05-15) ### :bug: Bug Fixes * fix memory leaks ([95be4ea](https://github.com/kazupon/vue-i18n/commit/95be4ea)), closes [#162](https://github.com/kazupon/vue-i18n/issues/162) ## [6.1.2](https://github.com/kazupon/vue-i18n/compare/v6.1.1...v6.1.2) (2017-05-15) ## [6.1.1](https://github.com/kazupon/vue-i18n/compare/v6.1.0...v6.1.1) (2017-04-19) ### :bug: Bug Fixes * **te:** Fix `te()` that always uses `this.locale`, even when `locale` supplied ([#147](https://github.com/kazupon/vue-i18n/issues/147)) by [@aicest](https://github.com/aicest) ([bf15eeb](https://github.com/kazupon/vue-i18n/commit/bf15eeb)), closes [#147](https://github.com/kazupon/vue-i18n/issues/147) # [6.1.0](https://github.com/kazupon/vue-i18n/compare/v6.0.0...v6.1.0) (2017-04-14) ### :star: New Features * **api:** add 'mergeLocaleMessage' method ([ef21621](https://github.com/kazupon/vue-i18n/commit/ef21621)), closes [#131](https://github.com/kazupon/vue-i18n/issues/131) * **silent:** add silent translation missing option ([29b3a17](https://github.com/kazupon/vue-i18n/commit/29b3a17)), closes [#139](https://github.com/kazupon/vue-i18n/issues/139) ### :zap: Improvements * change to method from computed property ([9135a59](https://github.com/kazupon/vue-i18n/commit/9135a59)), closes [#141](https://github.com/kazupon/vue-i18n/issues/141) # [6.0.0](https://github.com/kazupon/vue-i18n/compare/v6.0.0-beta.1...v6.0.0) (2017-04-05) :tada: :tada: :tada: See the [docs](https://kazupon.github.io/vue-i18n/en/) ### :zap: Improvements - Server-Side Rendering: [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/ssr) - Custom formatter: [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/formatting/custom) ### :star: NEW Features - Single File Components: [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/sfc) ### :boom: Breaking changes - API - Dynamic locale DEPRECATED # [6.0.0-beta.1](https://github.com/kazupon/vue-i18n/compare/v6.0.0-alpha.6...v6.0.0-beta.1) (2017-03-22) ### :boom: Breaking changes * change `fallbackRoot` and `sync` option default `true` value ([0890b44](https://github.com/kazupon/vue-i18n/commit/0890b44)) * remove messages settter, and add getLocaleMessage API ([0f0914d](https://github.com/kazupon/vue-i18n/commit/0f0914d)) ### :bug: Bug Fixes * **mixin:** fix computed props errors ([a6b7e37](https://github.com/kazupon/vue-i18n/commit/a6b7e37)) ### :up: Updates * **flowtype:** argument names ([cf14425](https://github.com/kazupon/vue-i18n/commit/cf14425)) ### :zap: Improvements * **fallbackLocale:** support reactivity ([ed758be](https://github.com/kazupon/vue-i18n/commit/ed758be)) * **warn:** suppress warning messages for production ([6e417d2](https://github.com/kazupon/vue-i18n/commit/6e417d2)) # [6.0.0-alpha.6](https://github.com/kazupon/vue-i18n/compare/v6.0.0-alpha.5...v6.0.0-alpha.6) (2017-03-16) ### :star: New Features * add 'setLocaleMessage' API ([8b71eda](https://github.com/kazupon/vue-i18n/commit/8b71eda)) # [6.0.0-alpha.5](https://github.com/kazupon/vue-i18n/compare/v6.0.0-alpha.3...v6.0.0-alpha.5) (2017-03-11) ### :bug: Bug Fixes * **mixin:** fix cannot create VueI18n instance error for minify production ([7eeb29f](https://github.com/kazupon/vue-i18n/commit/7eeb29f)) # [6.0.0-alpha.4](https://github.com/kazupon/vue-i18n/compare/v6.0.0-alpha.3...v6.0.0-alpha.4) (2017-03-11) # [6.0.0-alpha.3](https://github.com/kazupon/vue-i18n/compare/v6.0.0-alpha.2...v6.0.0-alpha.3) (2017-03-08) ### :star: New Features * add `sync` option ([5c46c07](https://github.com/kazupon/vue-i18n/commit/5c46c07)) ### :zap: Improvements * **mixin:** add error throwings and a warning ([0e4ac39](https://github.com/kazupon/vue-i18n/commit/0e4ac39)) # [6.0.0-alpha.2](https://github.com/kazupon/vue-i18n/compare/v6.0.0-alpha.1...v6.0.0-alpha.2) (2017-02-27) ### :zap: Improvements * **mixin:** release i18n instance ([cc362a3](https://github.com/kazupon/vue-i18n/commit/cc362a3)) * **vue:** support vue 2.2 ([5e7bf5e](https://github.com/kazupon/vue-i18n/commit/5e7bf5e)) # [6.0.0-alpha.1](https://github.com/kazupon/vue-i18n/compare/v5.0.2...v6.0.0-alpha.1) (2017-02-23) This is the first release of 6.0. In this version, we are some big breaking changes. - Recommended for: experiments, prototypes, upgrading small, non-critical apps - **NOT** recommended for: production use, upgrading production apps :warning: Documentation still needs to be worked on. And also, we might change some APIs and features. In the examples, please refer to this [examples](https://github.com/kazupon/vue-i18n/tree/dev/examples) directory. ## Improvements - Server-Side Rendering: [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/formatting/custom) - Custom formatter: [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/ssr) ## Features - Formatting support - Pluralization support - Locale and KeyPath Syntax support - Linked translation support - Fallback translation support - Component locale support - Dynamic locale DEPRECATED - Hot reload support ## API ### Global Config - Vue.config.lang DEPRECATED, use VueI18n constructor `locale` option, or VueI18n#locale - Vue.config.fallbackLang DEPRECATED, use VueI18n constructor `fallbackLocale` option, or VueI18n#fallbackLocale - Vue.config.missingHandler DEPRECATED, use VueI18n constructor `missing` option, or VueI18n#missing - Vue.config.i18nFormatter DEPRECATED, use VueI18n constructor `formatter` option, or VueI18n#formatter ### Global Method - Vue.locale DEPRECATED, use VueI18n constructor `messages` option, or VueI18n#messages - Vue.t DEPRECATED, use VueI18n#t - Vue.tc DEPRECATED, use VueI18n#tc - Vue.te DEPRECATED, use VueI18n#te ### Constructor Options - locales DEPRECATED, use `messages` of `i18n` option (e.g `{ i18n: { messaes: ... } }`) ### Instance Properties - $lang DEPRECATED, use `locale` of Vue instance property `$i18n` (e.g `vm.$i18n.locale = 'en'`) ### VueI18n class NEW - constructor options: See the [`I18nOptions` type](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js#L7-L15) of flowtype. - methods / properties: See the [`I18n` interface definition](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js#L17-L33) of flowtype. ## [5.0.2](https://github.com/kazupon/vue-i18n/compare/v5.0.1...v5.0.2) (2017-02-18) ### :zap: Improvements * **npm:** revert node >= 6.0 engine restriction ([#110](https://github.com/kazupon/vue-i18n/issues/110)) by [@syxolk](https://github.com/syxolk) ([92b1bd1](https://github.com/kazupon/vue-i18n/commit/92b1bd1)), closes [#109](https://github.com/kazupon/vue-i18n/issues/109) ## [5.0.1](https://github.com/kazupon/vue-i18n/compare/v5.0.0...v5.0.1) (2017-02-16) ### :zap: Improvements * **asset:** update locale reactivity setting ([b42fd9a](https://github.com/kazupon/vue-i18n/commit/b42fd9a)) # [5.0.0](https://github.com/kazupon/vue-i18n/compare/v4.10.0...v5.0.0) (2017-02-04) ### :boom: Breaking changes * drop vue 1.0 supporting ([4da26cf](https://github.com/kazupon/vue-i18n/commit/4da26cf)), closes [#105](https://github.com/kazupon/vue-i18n/issues/105) # [4.10.0](https://github.com/kazupon/vue-i18n/compare/v4.9.0...v4.10.0) (2017-01-01) ### :star: New Features * `$lang` property for all component ([#99](https://github.com/kazupon/vue-i18n/issues/99)) by [@albert](https://github.com/albert)-zhang ([5ed69f8](https://github.com/kazupon/vue-i18n/commit/5ed69f8)) ### :up: Updates * **override:** change langVM keeping variable name ([3ec1bb2](https://github.com/kazupon/vue-i18n/commit/3ec1bb2)) # [4.9.0](https://github.com/kazupon/vue-i18n/compare/v4.8.0...v4.9.0) (2016-12-17) ### :bug: Bug Fixes * **path:** fix nested key translation ([e15ead4](https://github.com/kazupon/vue-i18n/commit/e15ead4)), closes [#97](https://github.com/kazupon/vue-i18n/issues/97) ### :star: New Features * add globally locale checking ([4cac8b9](https://github.com/kazupon/vue-i18n/commit/4cac8b9)) * locale checking ([#98](https://github.com/kazupon/vue-i18n/issues/98)) by [@long](https://github.com/long)-long-float ([0bc0a6b](https://github.com/kazupon/vue-i18n/commit/0bc0a6b)) # [4.8.0](https://github.com/kazupon/vue-i18n/compare/v4.7.4...v4.8.0) (2016-12-08) ### :zap: Improvements * **extend:** disable no translation warning when set missingHandler ([168a97c](https://github.com/kazupon/vue-i18n/commit/168a97c)), closes [#96](https://github.com/kazupon/vue-i18n/issues/96) ## [4.7.4](https://github.com/kazupon/vue-i18n/compare/v4.7.3...v4.7.4) (2016-11-29) ### :bug: Bug Fixes * **extend:** fix interpolate error [@tariq86](https://github.com/tariq86) ([5f24e17](https://github.com/kazupon/vue-i18n/commit/5f24e17)) ## [4.7.3](https://github.com/kazupon/vue-i18n/compare/v4.7.2...v4.7.3) (2016-11-24) ### :bug: Bug Fixes * **extend:** fix array local ([35c268a](https://github.com/kazupon/vue-i18n/commit/35c268a)), closes [#91](https://github.com/kazupon/vue-i18n/issues/91) [#59](https://github.com/kazupon/vue-i18n/issues/59) ## [4.7.2](https://github.com/kazupon/vue-i18n/compare/v4.7.1...v4.7.2) (2016-11-19) ### :bug: Bug Fixes * **observer:** fix dep undefined error ([#88](https://github.com/kazupon/vue-i18n/issues/88)) by [@fandaa](https://github.com/fandaa) ([724974e](https://github.com/kazupon/vue-i18n/commit/724974e)), closes [#88](https://github.com/kazupon/vue-i18n/issues/88) ### :zap: Improvements * **extend:** support translate empty string ([#86](https://github.com/kazupon/vue-i18n/issues/86)) by [@QingWei](https://github.com/QingWei)-Li ([8e6d154](https://github.com/kazupon/vue-i18n/commit/8e6d154)) ## [4.7.1](https://github.com/kazupon/vue-i18n/compare/v4.7.0...v4.7.1) (2016-10-29) ### :bug: Bug Fixes * **interpolate:** named formatting: use name if value is missing ([#77](https://github.com/kazupon/vue-i18n/issues/77)) by [@SebastianS90](https://github.com/SebastianS90) ([a0cc343](https://github.com/kazupon/vue-i18n/commit/a0cc343)) ### :zap: Improvements * **named:** using default use nmae when value is missing ([c34e8f1](https://github.com/kazupon/vue-i18n/commit/c34e8f1)) # [4.7.0](https://github.com/kazupon/vue-i18n/compare/v4.6.0...v4.7.0) (2016-10-28) ### :star: New Features * hot reloading ([#71](https://github.com/kazupon/vue-i18n/issues/71)) by [@gglnx](https://github.com/gglnx) ([7bb94ac](https://github.com/kazupon/vue-i18n/commit/7bb94ac)) ### :zap: Improvements * **pluralization:** zero choice ([#70](https://github.com/kazupon/vue-i18n/issues/70)) by [@sebwas](https://github.com/sebwas) ([5f0004f](https://github.com/kazupon/vue-i18n/commit/5f0004f)) # [4.6.0](https://github.com/kazupon/vue-i18n/compare/v4.5.0...v4.6.0) (2016-09-24) ### :star: New Features * **config:** custom message formatter ([#57](https://github.com/kazupon/vue-i18n/issues/57)) by [@jvmccarthy](https://github.com/jvmccarthy) ([2748eb4](https://github.com/kazupon/vue-i18n/commit/2748eb4)) # [4.5.0](https://github.com/kazupon/vue-i18n/compare/v4.4.1...v4.5.0) (2016-09-15) ### :star: New Features * **config:** translation miss capturing configration ([aca0ed6](https://github.com/kazupon/vue-i18n/commit/aca0ed6)), closes [#54](https://github.com/kazupon/vue-i18n/issues/54) ## [4.4.1](https://github.com/kazupon/vue-i18n/compare/v4.4.0...v4.4.1) (2016-09-10) ### :zap: Improvements * **translate:** support hyphenated key ([#52](https://github.com/kazupon/vue-i18n/issues/52)) by [@tariq86](https://github.com/tariq86) ([a40acfd](https://github.com/kazupon/vue-i18n/commit/a40acfd)) # [4.4.0](https://github.com/kazupon/vue-i18n/compare/v4.3.1...v4.4.0) (2016-08-29) ### :star: New Features * add linked translations ([#50](https://github.com/kazupon/vue-i18n/issues/50)) by [@mmochetti](https://github.com/mmochetti) ([f7ae073](https://github.com/kazupon/vue-i18n/commit/f7ae073)) ## [4.3.1](https://github.com/kazupon/vue-i18n/compare/v4.3.0...v4.3.1) (2016-08-26) ### :bug: Bug Fixes * **npm:** fix installing bug ([57e66aa](https://github.com/kazupon/vue-i18n/commit/57e66aa)), closes [#46](https://github.com/kazupon/vue-i18n/issues/46) # [4.3.0](https://github.com/kazupon/vue-i18n/compare/v4.2.3...v4.3.0) (2016-08-26) ### :star: New Features * add pluralization ([#44](https://github.com/kazupon/vue-i18n/issues/44)) by [@mmochetti](https://github.com/mmochetti) ([b5b84d8](https://github.com/kazupon/vue-i18n/commit/b5b84d8)) ## [4.2.3](https://github.com/kazupon/vue-i18n/compare/v4.2.2...v4.2.3) (2016-08-23) ### :chart_with_upwards_trend: Performance Fixes * improve re-rendering cost when change the lang ([0707338](https://github.com/kazupon/vue-i18n/commit/0707338)) ## [4.2.2](https://github.com/kazupon/vue-i18n/compare/v4.2.1...v4.2.2) (2016-08-15) ### :bug: Bug Fixes * **path:** fix array path syntax error ([bc9dbee](https://github.com/kazupon/vue-i18n/commit/bc9dbee)), closes [#42](https://github.com/kazupon/vue-i18n/issues/42) [#43](https://github.com/kazupon/vue-i18n/issues/43) ## [4.2.1](https://github.com/kazupon/vue-i18n/compare/v4.2.0...v4.2.1) (2016-08-13) ### :zap: Improvements * **translate:** fallback translation warning ([5f6b271](https://github.com/kazupon/vue-i18n/commit/5f6b271)) # [4.2.0](https://github.com/kazupon/vue-i18n/compare/v4.1.0...v4.2.0) (2016-08-12) ### :chart_with_upwards_trend: Performance Fixes * **format:** use hasOwn function of Vue.util ([a8a19a0](https://github.com/kazupon/vue-i18n/commit/a8a19a0)) ### :star: New Features * **fallback:** add fallback translation feature ([1d1f0f2](https://github.com/kazupon/vue-i18n/commit/1d1f0f2)), closes [#36](https://github.com/kazupon/vue-i18n/issues/36) # [4.1.0](https://github.com/kazupon/vue-i18n/compare/v4.0.1...v4.1.0) (2016-07-25) ### :bug: Bug Fixes * **util:** fixed isArray reference errors ([0c6f6a0](https://github.com/kazupon/vue-i18n/commit/0c6f6a0)) ### :star: New Features * support vue 2.0.0.beta later ([0e1d2f7](https://github.com/kazupon/vue-i18n/commit/0e1d2f7)) ## [4.0.1](https://github.com/kazupon/vue-i18n/compare/v4.0.0...v4.0.1) (2016-06-06) ### :bug: Bug Fixes * **translate:** fix underscore named argument translate issue ([eeaf936](https://github.com/kazupon/vue-i18n/commit/eeaf936)) # [4.0.0](https://github.com/kazupon/vue-i18n/compare/v3.1.1...v4.0.0) (2016-05-10) ### :zap: Improvements * support vue 2.0-pre-alpha ([f6517bc](https://github.com/kazupon/vue-i18n/commit/f6517bc)) ## [3.1.1](https://github.com/kazupon/vue-i18n/compare/v3.1.0...v3.1.1) (2016-05-09) ### :star: New Features * auto installation for standalone ([2b0dc09](https://github.com/kazupon/vue-i18n/commit/2b0dc09)) # [3.1.0](https://github.com/kazupon/vue-i18n/compare/v3.0.0...v3.1.0) (2016-05-09) ### :star: New Features * component locales ([12fe695](https://github.com/kazupon/vue-i18n/commit/12fe695)), closes [#29](https://github.com/kazupon/vue-i18n/issues/29) ### :warning: Depcreted * **options:** remove Vue.use options ([d87b59b](https://github.com/kazupon/vue-i18n/commit/d87b59b)) ### :zap: Improvements * **keypath:** port the object path parser ([3ae04b7](https://github.com/kazupon/vue-i18n/commit/3ae04b7)) * **translation:** fix hypenate included key translating ([d0a415f](https://github.com/kazupon/vue-i18n/commit/d0a415f)), closes [#24](https://github.com/kazupon/vue-i18n/issues/24) * **translation:** warning outputing when cannot translate with keypath ([b4c7c0e](https://github.com/kazupon/vue-i18n/commit/b4c7c0e)), closes [#22](https://github.com/kazupon/vue-i18n/issues/22) # [3.0.0](https://github.com/kazupon/vue-i18n/compare/v2.4.1...v3.0.0) (2016-04-18) ### Features * **lang:** support lang reactive changing ([203ee85](https://github.com/kazupon/vue-i18n/commit/203ee85)), closes [#2](https://github.com/kazupon/vue-i18n/issues/2) [#15](https://github.com/kazupon/vue-i18n/issues/15) * **locale:** support dynamic local ([4d61e8d](https://github.com/kazupon/vue-i18n/commit/4d61e8d)), closes [#6](https://github.com/kazupon/vue-i18n/issues/6) [#21](https://github.com/kazupon/vue-i18n/issues/21) ### DEPRECATED * **index:** plugin install `Vue.use` options (`options.locales`, `options.lang`). See [README](https://github.com/kazupon/vue-i18n/blob/dev/README.md) ## [2.4.1](https://github.com/kazupon/vue-i18n/compare/v2.4.0...v2.4.1) (2016-02-29) ### Features * **i18n:** support ruby on rails i18n interpolation format ([b6b2490](https://github.com/kazupon/vue-i18n/commit/b6b2490)) # [2.4.0](https://github.com/kazupon/vue-i18n/compare/v2.3.3...v2.4.0) (2016-02-06) ### Features * **i18n:** add Vue.t function ([68935e3](https://github.com/kazupon/vue-i18n/commit/68935e3)), closes [#17](https://github.com/kazupon/vue-i18n/issues/17) ## [2.3.3](https://github.com/kazupon/vue-i18n/compare/v2.3.2...v2.3.3) (2015-12-09) ### Bug Fixes * **npm:** npm install error ([e31e89e](https://github.com/kazupon/vue-i18n/commit/e31e89e)) ### Features * **bower:** good-bye bower :wink: ([d99eb15](https://github.com/kazupon/vue-i18n/commit/d99eb15)) ### BREAKING CHANGES * bower: not support `bower` package manager I think that bower is dead. :no_good: ## [2.3.2](https://github.com/kazupon/vue-i18n/compare/v2.3.1...v2.3.2) (2015-12-09) ### Features * **bundle:** more compact the vue-i18n distribution file ([2f32ecc](https://github.com/kazupon/vue-i18n/commit/2f32ecc)) ## [2.3.1](https://github.com/kazupon/vue-i18n/compare/v2.3.0...v2.3.1) (2015-12-01) ### Reverts * **index:** automatically install for standalone ([25b8059](https://github.com/kazupon/vue-i18n/commit/25b8059)) # [2.3.0](https://github.com/kazupon/vue-i18n/compare/v2.2.0...v2.3.0) (2015-11-26) ### Bug Fixes * **index:** cannot work at Vue 1.0.10 later ([6fd543e](https://github.com/kazupon/vue-i18n/commit/6fd543e)), closes [#9](https://github.com/kazupon/vue-i18n/issues/9) ### Features * **index:** support automatically install for standalone ([ada2673](https://github.com/kazupon/vue-i18n/commit/ada2673)) # v2.2.0 / 2015-09-16 * Re-implemetation with ES6 (babel) # v2.1.0 / 2015-07-03 * Add global local language setting with `Vue.config.lang` # v2.0.0 / 2015-06-29 * Support Vue.js 0.12 * Remove the followings (Breaking Changes) * `Vue.t` function * `v-t` directive # v1.1.1 / 2015-04-21 * Fix unit test error # v1.1.0 / 2015-01-10 * Support template string in `$t` method * Support language changing in `$t` method # v1.0.0 / 2015-01-10 * Add `$t` method # v0.11.0 / 2014-11-07 * Bump to 0.11.0 # v0.2.0 / 2014-10-08 * Support Vue.js 0.11.0-rc # v0.1.2 / 2014-10-07 * Support bower # v0.1.1 / 2014-10-06 * Add `Vue.t` function # v0.1.0 / 2014-05-06 * Release first # v0.0.0 / 2014-05-03 * Initial project ================================================ FILE: CONTRIBUTING.md ================================================ # vue-i18n Contributing Guide - [Issue Reporting Guidelines](#issue-reporting-guidelines) - [Pull Request Guidelines](#pull-request-guidelines) - [Development Setup](#development-setup) ## General Guidelines Thanks for understanding that English is used as a shared language in this repository. Maintainers do not use machine translation to avoid miscommunication due to error in translation. If description of issue / PR are written in non-English languages, those may be closed. It is ofcourse fine to use non-English language, when you open a PR to translate documents and communicates with other users in same language. ## Issue Reporting Guidelines - The issue list of this repo is **exclusively** for bug reports and feature requests. Non-conforming issues will be closed immediately. - For simple beginner questions, you can get quick answers from [`#vue-i18n` channel of Discord](https://chat.vuejs.org/) - For more complicated questions, you can use [the official forum](http://forum.vuejs.org/) or StackOverflow. Make sure to provide enough information when asking your questions - this makes it easier for others to help you! - Try to search for your issue, it may have already been answered or even fixed in the development branch. - Check if the issue is reproducible with the latest stable version of Vue. If you are using a pre-release, please indicate the specific version you are using. - It is **required** that you clearly describe the steps necessary to reproduce the issue you are running into. Issues with no clear repro steps will not be triaged. If an issue labeled "need repro" receives no further input from the issue author for more than 5 days, it will be closed. - It is recommended that you make a JSFiddle/JSBin/Codepen/CodeSandbox to demonstrate your issue. You could start based with [this template](http://jsfiddle.net/r8qnsfb1/) that already includes the latest version. - For bugs that involves build setups, you can create a reproduction repository with steps in the README. - If your issue is resolved but still open, don’t hesitate to close it. In case you found a solution by yourself, it could be helpful to explain how you fixed it. ## Pull Request Guidelines - The `master` branch is basically just a snapshot of the latest stable release. All development should be done in dedicated branches. **Do not submit PRs against the `master` branch.** - Checkout a topic branch from the relevant branch, e.g. `v8.x`, and merge back against that branch. - Work in the `src` folder and **DO NOT** checkin `dist` in the commits. - It's OK to have multiple small commits as you work on the PR - we will let GitHub automatically squash it before merging. - Make sure `npm test` passes. (see [development setup](#development-setup)) - If adding new feature: - Add accompanying test case. - Provide convincing reason to add this feature. Ideally you should open a suggestion issue first and have it greenlighted before working on it. - If fixing a bug: - Provide detailed description of the bug in the PR. Live demo preferred. - Add appropriate test coverage if applicable. ### Work Step Example - Fork the repository from [kazupon/vue-i18n](https://github.com/kazupon/vue-i18n) ! - Create your topic branch from `v8.x`: `git branch my-new-topic origin/v8.x` - Add codes and pass tests ! - Commit your changes: `git commit -am 'Add some topic'` - Push to the branch: `git push origin my-new-topic` - Submit a pull request to `v8.x` branch of `kazupon/vue-i18n` repository ! ## Development Setup You will need [Node.js](http://nodejs.org) and [Java Runtime Environment](http://www.oracle.com/technetwork/java/javase/downloads/index.html) (needed for running Selenium server during e2e tests). After cloning the repo, run: $ npm install ### Commonly used NPM scripts # watch and serve with hot reload unit test at localhost:8080 $ npm run dev # lint source codes $ npm run lint # run unit tests in browser (firefox/safari/chrome) $ npm run test:unit # build all dist files, including npm packages $ npm run build # run the full test suite, include linting / type checking $ npm test There are some other scripts available in the `scripts` section of the `package.json` file. The default test script will do the following: lint with ESLint -> type check with Flow -> unit tests with coverage -> e2e tests. **Please make sure to have this pass successfully before submitting a PR.** Although the same tests will be run against your PR on the CI server, it is better to have it working locally beforehand. ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2016 kazuya kawaguchi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================

Vue I18n logo

vue-i18n

Test Status NPM version vue-i18n channel on Discord

Internationalization plugin for Vue.js


> [!IMPORTANT] > Vue I18n v8 has reached EOL and is no longer actively maintained. Please upgrade to Vue I18n v9 or later. **The seucirty hotfix is only provided for Vue I18n v8.28 until end of 2024**. Thereafter, security fix is not provide for that version later. However, if it is absolutely necessary, please contact to contact@frapwings.jp

🏅 Platinum Sponsors

✨ Special Sponsors

🥇 Gold Sponsors

🥈 Silver Sponsors

🥉 Bronze Sponsors


## ⚠️ NOTICE **This repository is for Vue I18n v8.x and Vue 2** **If you want to know about how to usage for Vue I18n v9 on Vue 3, See the [this repository](https://github.com/intlify/vue-i18n-next))** ## 🙋‍♂️ About support for v8 We will follow Vue v2 maintenance lifespan ## 📔 Documentation About Vue I18n v8.x, See [here](http://kazupon.github.io/vue-i18n/) If you want to read Vue I18n v9 docs, See [here](https://vue-i18n.intlify.dev/) ## 📜 Changelog Detailed changes for each release are documented in the [CHANGELOG.md](https://github.com/kazupon/vue-i18n/blob/dev/CHANGELOG.md). ## ❗ Issues Please make sure to read the [Issue Reporting Checklist](https://github.com/kazupon/vue-i18n/blob/dev/CONTRIBUTING.md#issue-reporting-guidelines) before opening an issue. Issues not conforming to the guidelines may be closed immediately. ## 💪 Contribution Please make sure to read the [Contributing Guide](https://github.com/kazupon/vue-i18n/blob/dev/CONTRIBUTING.md) before making a pull request. ## ©️ License [MIT](http://opensource.org/licenses/MIT) ================================================ FILE: SECURITY.md ================================================ # Security Policy ## Supported Versions | Version | Supported | | ------- | ------------------ | | 8.28.x | :white_check_mark: | | < 8.28.0 | :x: | ## Reporting a Vulnerability To report a vulnerability, please [privately report it via the Security tab](https://github.com/kazupon/vue-i18n/security/advisories/new) on the correct GitHub repository (see [documentation](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability)). If that is impossible, feel free to send an email to contact@frapwings.jp instead. All security vulnerabilities will be promptly verified and addressed. ================================================ FILE: assets/README.md ================================================ # vue-i18n-art > Vue I18n artworks Creative Commons License
Vue I18n artworks by Kazuya Kawaguchi is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License, with the following extra conditions: - It is **OK** to use the Vue I18n logo in the following cases: - in marketing materials for technical events, e.g. meetups, hackathons, conferences and workshops that are related to Vue I18n. - in open source projects related to Vue I18n. - in technical articles / videos / books / papers for educational purposes. - to illustrate a commercial product's Vue I18n integration in its marketing copy. - It is **NOT OK** to use the Vue I18n logo in the following cases without prior written consent from the copyright owner: - Using the Vue I18n logo in a commercial product for purposes other than illustrating its Vue I18n integration. - Sell physical products that uses the Vue I18n logo or its variants, e.g. t-shirts. The copyright owner reserves the right of final explanation for any use case not explicitly stated above. ================================================ FILE: config/.eslintrc.js ================================================ module.exports = { globals: { process: true }, extends: 'vue', rules: { 'no-multiple-empty-lines': [2, {max: 2}], 'no-console': 0 } } ================================================ FILE: config/banner.js ================================================ const pack = require('../package.json') const version = process.env.VERSION || pack.version module.exports = '/*!\n' + ` * ${pack.name} v${version} \n` + ` * (c) ${new Date().getFullYear()} ${pack.author.name}\n` + ` * Released under the ${pack.license} License.\n` + ' */' ================================================ FILE: config/build.js ================================================ 'use strict' const fs = require('fs') const exist = fs.existsSync const mkdir = fs.mkdirSync const getAllEntries = require('./entry').getAllEntries const build = require('./bundle') if (!exist('dist')) { mkdir('dist') } let entries = getAllEntries() // filter entries via command line arg if (process.argv[2]) { const filters = process.argv[2].split(',') entries = entries.filter(b => { return filters.some(f => b.dest.indexOf(f) > -1) }) } build(entries) ================================================ FILE: config/bundle.js ================================================ const fs = require('fs') const path = require('path') const zlib = require('zlib') const rollup = require('rollup') const terser = require('terser') module.exports = build function build (entries) { let built = 0 const total = entries.length const next = () => { buildEntry(entries[built]).then(() => { built++ if (built < total) { next() } }).catch(logError) } next() } function buildEntry (config) { const output = config.output const { file, banner } = output const isProd = /min\.js$/.test(file) return rollup.rollup(config) .then(bundle => bundle.generate(output)) .then(({ code }) => { if (isProd) { var minified = (banner ? banner + '\n' : '') + terser.minify(code, { toplevel: true, output: { ascii_only: true }, compress: { pure_funcs: ['makeMap'] } }).code return write(file, minified, true) } else { return write(file, code) } }) } function write (dest, code, zip) { return new Promise((resolve, reject) => { function report (extra) { console.log(blue(path.relative(process.cwd(), dest)) + ' ' + getSize(code) + (extra || '')) resolve() } fs.writeFile(dest, code, err => { if (err) { return reject(err) } if (zip) { zlib.gzip(code, (err, zipped) => { if (err) { return reject(err) } report(' (gzipped: ' + getSize(zipped) + ')') }) } else { report() } }) }) } function getSize (code) { return (code.length / 1024).toFixed(2) + 'kb' } function logError (e) { console.log(e) } function blue (str) { return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m' } ================================================ FILE: config/entry.js ================================================ const path = require('path') const replace = require('rollup-plugin-replace') const flow = require('rollup-plugin-flow-no-whitespace') const buble = require('rollup-plugin-buble') const node = require('rollup-plugin-node-resolve') const cjs = require('rollup-plugin-commonjs') const banner = require('./banner') const pack = require('../package.json') function toUpper (_, c) { return c ? c.toUpperCase() : '' } const resolve = _path => path.resolve(__dirname, '../', _path) const classifyRE = /(?:^|[-_\/])(\w)/g function classify (str) { return str.replace(classifyRE, toUpper) } const moduleName = classify(pack.name) const entries = { commonjs: { entry: resolve('src/index.js'), dest: resolve(`dist/${pack.name}.common.js`), format: 'cjs', banner }, esm: { entry: 'src/index.js', dest: resolve(`dist/${pack.name}.esm.js`), format: 'es', banner }, production: { entry: 'src/index.js', dest: resolve(`dist/${pack.name}.min.js`), format: 'umd', env: 'production', moduleName, banner }, development: { entry: 'src/index.js', dest: resolve(`dist/${pack.name}.js`), format: 'umd', env: 'development', moduleName, banner }, browser_development: { entry: 'src/index.js', dest: resolve(`dist/${pack.name}.esm.browser.js`), format: 'es', env: 'development', moduleName, transpile: false }, browser_production: { entry: 'src/index.js', dest: resolve(`dist/${pack.name}.esm.browser.min.js`), format: 'es', env: 'production', moduleName, transpile: false } } function genConfig (opts) { const config = { input: opts.entry, output: { file: opts.dest, name: moduleName, format: opts.format, banner: opts.banner }, plugins: [ flow(), node(), cjs() ] } const replacePluginOptions = { '__VERSION__': pack.version } if (opts.env) { replacePluginOptions['process.env.NODE_ENV'] = JSON.stringify(opts.env) } config.plugins.push(replace(replacePluginOptions)) if (opts.transpile !== false) { config.plugins.push(buble()) } return config } exports.getEntry = name => genConfig(entries[name]) exports.getAllEntries = () => Object.keys(entries).map(name => genConfig(entries[name])) ================================================ FILE: config/karma-intl-plugin.js ================================================ const path = require('path') const init = function (files) { files.unshift({ pattern: path.join(__dirname, '../node_modules/intl/dist/Intl.complete.js'), included: true, served: true, watched: false }) } init.$inject = ['config.files'] module.exports = { 'framework:intl-shim': ['factory', init] } ================================================ FILE: config/karma.base.conf.js ================================================ const webpack = require('webpack') const intlPlugin = require('./karma-intl-plugin') const webpackConfig = { module: { rules: [{ test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }] }, devtool: '#inline-source-map' } module.exports = { frameworks: ['mocha', 'intl-shim'], files: [ '../test/unit/index.js' ], preprocessors: { '../test/unit/index.js': ['webpack', 'sourcemap'] }, webpack: Object.assign({ mode: 'development', }, webpackConfig), webpackMiddleware: { noInfo: true }, plugins: [ 'karma-mocha', 'karma-mocha-reporter', 'karma-sourcemap-loader', 'karma-webpack', intlPlugin, ] } ================================================ FILE: config/karma.cover.conf.js ================================================ const base = require('./karma.base.conf') module.exports = config => { const options = Object.assign(base, { browsers: ['ChromeHeadless'], reporters: ['mocha', 'coverage'], coverageReporter: { reporters: [ { type: 'lcov', dir: '../coverage', subdir: '.' }, { type: 'text-summary', dir: '../coverage', subdir: '.' } ] }, singleRun: true, plugins: base.plugins.concat([ 'karma-coverage', 'karma-chrome-launcher' ]) }) config.set(options) } ================================================ FILE: config/karma.sauce.conf.js ================================================ const base = require('./karma.base.conf') const pack = require('../package.json') /** * Having too many tests running concurrently on saucelabs * causes timeouts and errors, so we have to run them in * smaller batches. */ const batches = [ // the coolkids { sl_chrome: { base: 'SauceLabs', browserName: 'chrome', platform: 'Windows 7' }, sl_firefox: { base: 'SauceLabs', browserName: 'firefox' }, sl_mac_safari: { base: 'SauceLabs', browserName: 'safari', platform: 'OS X 10.10' } }, // ie family { sl_ie_9: { base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 7', version: '9' }, sl_ie_10: { base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 8', version: '10' }, sl_ie_11: { base: 'SauceLabs', browserName: 'internet explorer', platform: 'Windows 8.1', version: '11' }, sl_edge: { base: 'SauceLabs', platform: 'Windows 10', browserName: 'MicrosoftEdge' } }, // mobile { sl_ios_safari_8: { base: 'SauceLabs', browserName: 'iphone', version: '8.4' }, sl_ios_safari_9: { base: 'SauceLabs', browserName: 'iphone', version: '9.3' }, sl_android_4_4: { base: 'SauceLabs', browserName: 'android', version: '4.4' }, sl_android_5_1: { base: 'SauceLabs', browserName: 'android', version: '5.1' } } ] module.exports = config => { const batch = batches[process.argv[5] || 0] config.set(Object.assign(base, { singleRun: true, browsers: Object.keys(batch), customLaunchers: batch, reporters: process.env.CI ? ['dots', 'saucelabs'] // avoid spamming CI output : ['progress', 'saucelabs'], sauceLabs: { testName: `${pack.name} unit tests`, recordScreenshots: false, connectOptions: { 'no-ssl-bump-domains': 'all' // Ignore SSL error on Android emulator }, build: process.env.CIRCLE_BUILD_NUM || process.env.SAUCE_BUILD_ID || Date.now() }, captureTimeout: 300000, browserNoActivityTimeout: 300000, plugins: base.plugins.concat([ 'karma-sauce-launcher' ]) })) } ================================================ FILE: config/karma.unit.ci.conf.js ================================================ const base = require('./karma.base.conf') module.exports = config => { config.set(Object.assign(base, { browsers: ['ChromeHeadless', 'FirefoxHeadless'], reporters: ['progress'], singleRun: true, plugins: base.plugins.concat([ 'karma-chrome-launcher', 'karma-firefox-launcher', ]) })) } ================================================ FILE: config/karma.unit.conf.js ================================================ const base = require('./karma.base.conf') module.exports = config => { config.set(Object.assign(base, { browsers: ['Chrome', 'Firefox', 'Safari'], reporters: ['progress'], singleRun: true, plugins: base.plugins.concat([ 'karma-chrome-launcher', 'karma-firefox-launcher', 'karma-safari-launcher' ]) })) } ================================================ FILE: config/nightwatch.conf.js ================================================ // http://nightwatchjs.org/guide#settings-file module.exports = { src_folders: ['test/e2e/test'], output_folder: 'test/e2e/report', custom_commands_path: ['node_modules/nightwatch-helpers/commands'], custom_assertions_path: ['node_modules/nightwatch-helpers/assertions'], selenium: { start_process: true, server_path: 'node_modules/selenium-server/lib/runner/selenium-server-standalone-3.141.59.jar', host: '127.0.0.1', port: 4444, cli_args: { 'webdriver.chrome.driver': require('chromedriver').path } }, test_settings: { default: { selenium_port: 4444, selenium_host: 'localhost', silent: true, screenshots: { enabled: true, on_failure: true, on_error: false, path: 'test/e2e/screenshots' } }, chrome: { desiredCapabilities: { browserName: 'chrome', javascriptEnabled: true, acceptSslCerts: true, chromeOptions: { // Issue https://github.com/nightwatchjs/nightwatch/issues/2149 w3c: false } } }, firefox: { desiredCapabilities: { browserName: 'firefox', javascriptEnabled: true, acceptSslCerts: true } }, headless: { desiredCapabilities: { browserName: 'chrome', chromeOptions : { args : ['headless'], w3c: false }, javascriptEnabled: true, acceptSslCerts: true } } } } ================================================ FILE: config/version.js ================================================ const fs = require('fs') const pack = require('../package.json') // update installation.md const installation = fs .readFileSync('./vuepress/installation.md', 'utf-8') .replace( /https:\/\/unpkg\.com\/vue-i18n@[\d.]+.[\d]+\/dist\/vue-i18n\.js/, 'https://unpkg.com/vue-i18n@' + pack.version + '/dist/vue-i18n.js' ) fs.writeFileSync('./vuepress/installation.md', installation) ================================================ FILE: config/webpack.dev.conf.js ================================================ const path = require('path') const webpack = require('webpack') module.exports = { entry: 'mocha-loader!./test/unit/index.js', mode: 'development', resolve: { extensions: ['.js'] }, output: { path: path.resolve(__dirname, '/test/unit'), filename: 'tests.js', publicPath: '/' }, module: { rules: [{ test: /\.js$/, exclude: /node_modules|vue\/dist/, loader: 'babel-loader' }] }, devtool: '#eval-source-map' } ================================================ FILE: decls/i18n.js ================================================ declare var Intl: any; declare type Path = string; declare type Locale = string; declare type MessageContext = { list: (index: number) => mixed, named: (key: string) => mixed, linked: (key: string) => TranslateResult, values: any, path: string, formatter: Formatter, messages: LocaleMessages, locale: Locale } declare type MessageFunction = (ctx: MessageContext) => string declare type FallbackLocale = string | string[] | false | { [locale: string]: string[] }; declare type LocaleMessage = string | MessageFunction | LocaleMessageObject | LocaleMessageArray; declare type LocaleMessageObject = { [key: Path]: LocaleMessage }; declare type LocaleMessageArray = Array; declare type LocaleMessages = { [key: Locale]: LocaleMessageObject }; // This options is the same as Intl.DateTimeFormat constructor options: // http://www.ecma-international.org/ecma-402/2.0/#sec-intl-datetimeformat-constructor declare type DateTimeFormatOptions = { year?: 'numeric' | '2-digit', month?: 'numeric' | '2-digit' | 'narrow' | 'short' | 'long', day?: 'numeric' | '2-digit', hour?: 'numeric' | '2-digit', minute?: 'numeric' | '2-digit', second?: 'numeric' | '2-digit', weekday?: 'narrow' | 'short' | 'long', hour12?: boolean, era?: 'narrow' | 'short' | 'long', timeZone?: string, // IANA time zone timeZoneName?: 'short' | 'long', localeMatcher?: 'lookup' | 'best fit', formatMatcher?: 'basic' | 'best fit' }; declare type DateTimeFormat = { [key: string]: DateTimeFormatOptions }; declare type DateTimeFormats = { [key: Locale]: DateTimeFormat }; // This options is the same as Intl.NumberFormat constructor options: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat declare type NumberFormatOptions = { style?: 'decimal' | 'currency' | 'percent', currency?: string, // ISO 4217 currency codes currencyDisplay?: 'symbol' | 'code' | 'name', useGrouping?: boolean, minimumIntegerDigits?: number, minimumFractionDigits?: number, maximumFractionDigits?: number, minimumSignificantDigits?: number, maximumSignificantDigits?: number, localeMatcher?: 'lookup' | 'best fit', formatMatcher?: 'basic' | 'best fit' }; declare type NumberFormat = { [key: string]: NumberFormatOptions }; declare type NumberFormats = { [key: Locale]: NumberFormat }; declare type Modifiers = { [key: string]: (str: string) => string }; declare type TranslateResult = string | LocaleMessages; declare type DateTimeFormatResult = string; declare type NumberFormatResult = string; declare type MissingHandler = (locale: Locale, key: Path, vm?: any) => string | void; declare type PostTranslationHandler = (str: string, key?: string) => string; declare type GetChoiceIndex = (choice: number, choicesLength: number) => number declare type ComponentInstanceCreatedListener = (newI18n: I18n, rootI18n: I18n) => void; declare type FormattedNumberPartType = 'currency' | 'decimal' | 'fraction' | 'group' | 'infinity' | 'integer' | 'literal' | 'minusSign' | 'nan' | 'plusSign' | 'percentSign'; declare type FormattedNumberPart = { type: FormattedNumberPartType, value: string, }; // This array is the same as Intl.NumberFormat.formatToParts() return value: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat/formatToParts#Return_value declare type NumberFormatToPartsResult = Array; declare type WarnHtmlInMessageLevel = 'off' | 'warn' | 'error'; declare type I18nOptions = { locale?: Locale, fallbackLocale?: FallbackLocale, messages?: LocaleMessages, dateTimeFormats?: DateTimeFormats, datetimeFormats?: DateTimeFormats, numberFormats?: NumberFormats, formatter?: Formatter, missing?: MissingHandler, modifiers?: Modifiers, root?: I18n, // for internal fallbackRoot?: boolean, fallbackRootWithEmptyString?: boolean, formatFallbackMessages?: boolean, sync?: boolean, silentTranslationWarn?: boolean | RegExp, silentFallbackWarn?: boolean | RegExp, pluralizationRules?: PluralizationRules, preserveDirectiveContent?: boolean, warnHtmlInMessage?: WarnHtmlInMessageLevel, sharedMessages?: LocaleMessage, postTranslation?: PostTranslationHandler, componentInstanceCreatedListener?: ComponentInstanceCreatedListener, escapeParameterHtml?: boolean, __VUE_I18N_BRIDGE__?: string, }; declare type IntlAvailability = { dateTimeFormat: boolean, numberFormat: boolean }; declare type PluralizationRules = { [lang: string]: GetChoiceIndex, } declare interface I18n { static install: () => void, // for Vue plugin interface static version: string, static availabilities: IntlAvailability, get vm (): any, // for internal get locale (): Locale, set locale (locale: Locale): void, get fallbackLocale (): FallbackLocale, set fallbackLocale (locale: FallbackLocale): void, get messages (): LocaleMessages, get dateTimeFormats (): DateTimeFormats, get numberFormats (): NumberFormats, get availableLocales (): Locale[], get missing (): ?MissingHandler, set missing (handler: MissingHandler): void, get formatter (): Formatter, set formatter (formatter: Formatter): void, get formatFallbackMessages (): boolean, set formatFallbackMessages (fallback: boolean): void, get silentTranslationWarn (): boolean | RegExp, set silentTranslationWarn (silent: boolean | RegExp): void, get silentFallbackWarn (): boolean | RegExp, set silentFallbackWarn (slient: boolean | RegExp): void, get pluralizationRules (): PluralizationRules, set pluralizationRules (rules: PluralizationRules): void, get preserveDirectiveContent (): boolean, set preserveDirectiveContent (preserve: boolean): void, get warnHtmlInMessage (): WarnHtmlInMessageLevel, set warnHtmlInMessage (level: WarnHtmlInMessageLevel): void, get postTranslation (): ?PostTranslationHandler, set postTranslation (handler: PostTranslationHandler): void, getLocaleMessage (locale: Locale): LocaleMessageObject, setLocaleMessage (locale: Locale, message: LocaleMessageObject): void, mergeLocaleMessage (locale: Locale, message: LocaleMessageObject): void, t (key: Path, ...values: any): TranslateResult, i (key: Path, locale: Locale, values: Object): TranslateResult, tc (key: Path, choice?: number, ...values: any): TranslateResult, te (key: Path, locale?: Locale): boolean, getDateTimeFormat (locale: Locale): DateTimeFormat, setDateTimeFormat (locale: Locale, format: DateTimeFormat): void, mergeDateTimeFormat (locale: Locale, format: DateTimeFormat): void, d (value: number | Date, ...args: any): DateTimeFormatResult, getNumberFormat (locale: Locale): NumberFormat, setNumberFormat (locale: Locale, format: NumberFormat): void, mergeNumberFormat (locale: Locale, format: NumberFormat): void, n (value: number, ...args: any): NumberFormatResult, getChoiceIndex: GetChoiceIndex, pluralizationRules: PluralizationRules, preserveDirectiveContent: boolean }; declare interface Formatter { interpolate (message: string, values: any, path: string): (Array | null) }; ================================================ FILE: decls/module.js ================================================ declare type $npm$Vue$Dictionaly = { [key: string]: T } declare type Util = { extend: (to: Object, from: ?Object) => Object, hasOwn: (obj: Object, key: string) => boolean, isPlainObject: (obj: any) => boolean, isObject: (obj: mixed) => boolean, } declare type Config = { optionMergeStrategies: $npm$Vue$Dictionaly, silent: boolean, productionTip: boolean, performance: boolean, devtools: boolean, errorHandler: ?(err: Error, vm: Vue, info: string) => void, ignoredElements: Array, keyCodes: $npm$Vue$Dictionaly, isReservedTag: (x?: string) => boolean, parsePlatformTagName: (x: string) => string, isUnknownElement: (x?: string) => boolean, getTagNamespace: (x?: string) => string | void, mustUseProp: (tag: string, type: ?string, name: string) => boolean, } declare interface Vue { static config: Config, static util: Util, static version: string, } ================================================ FILE: dist/README.md ================================================ ## Explanation of Different Builds - UMD: `vue-i18n.js` - CommonJS: `vue-i18n.common.js` - ES Module for bundlers: `vue-i18n.esm.js` - ES Module for browsers: `vue-i18n.esm.browser.js` ### Terms - **[UMD](https://github.com/umdjs/umd)**: UMD builds can be used directly in the browser via a ` ================================================ FILE: docs/api/index.html ================================================ API references | Vue I18n

# API references

# Extension of Vue

# Vue constructor options

# i18n

  • Type: I18nOptions

Component based localization option.

  • See also: VueI18n class constructor options

# Vue injected methods

# $t

  • Arguments:

    • {Path} key: required
    • {Locale} locale: optional
    • {Array | Object} values: optional
  • Return: TranslateResult

Localize the locale message of key. Localize in preferentially component locale messages than global locale messages. If not specified component locale messages, localize with global locale messages. If you specified locale, localize the locale messages of locale. If you specified key of list / named formatting local messages, you must specify values too. For values more details see Formatting.

Tip

Note that you need to guarantee this context equal to component instance in lifecycle methods (e.g. in data options, const $t = this.$t.bind(this)).

# $tc

  • Arguments:

    • {Path} key: required
    • {number} choice: optional, default 1
    • {Locale} locale: optional
    • {string | Array | Object} values: optional
  • Return: TranslateResult

Localize the locale message of key with pluralization. Localize in preferentially component locale messages than global locale messages. If not specified component locale messages, localize with global locale messages. If you specified locale, localize the locale messages of locale. If you will specify string value to values, localize the locale messages of value. If you will specify Array or Object value to values, you must specify with values of $t.

Tip

Note that you need to guarantee this context equal to component instance in lifecycle methods (e.g. in data options, const $tc = this.$tc.bind(this)).

# $te

  • Arguments:

    • {Path} key: required
    • {Locale} locale: optional
  • Return: boolean

Check whether key exists. In Vue instance, If not specified component locale messages, check with global locale messages. If you specified locale, check the locale messages of locale.

Tip

Note that you need to guarantee this context equal to component instance in lifecycle methods (e.g. in data options, const $te = this.$te.bind(this)).

# $d

🆕 7.0+

  • Arguments:

    • {number | Date} value: required
    • {Path | Object} key: optional
    • {Locale | Object} locale: optional
  • Return: DateTimeFormatResult

Localize the datetime of value with datetime format of key. The datetime format of key need to register to dateTimeFormats option of VueI18n class, and depend on locale option of VueI18n constructor. If you will specify locale argument, it will have priority over locale option of VueI18n constructor.

If the datetime format of key not exist in dateTimeFormats option, fallback to depend on fallbackLocale option of VueI18n constructor.

Tip

Note that you need to guarantee this context equal to component instance in lifecycle methods (e.g. in data options, const $d = this.$d.bind(this)).

# $n

🆕 7.0+

  • Arguments:

    • {number} value: required
    • {Path | Object} format: optional
    • {Locale} locale: optional
  • Return: NumberFormatResult

Localize the number of value with number format of format. The number format of format need to register to numberFormats option of VueI18n class, and depend on locale option of VueI18n constructor. If you will specify locale argument, it will have priority over locale option of VueI18n constructor.

If the number format of format not exist in numberFormats option, fallback to depend on fallbackLocale option of VueI18n constructor.

If the second format argument specified as an object, it should have the following properties:

  • key {Path}: optional, number format
  • locale {Locale}: optional, locale
  • style {string}: optional, number format option
  • currency {string}: optional, number format option
  • currencyDisplay {string}: optional, number format option
  • useGrouping {string}: optional, number format option
  • minimumIntegerDigits {string}: optional, number format option
  • minimumFractionDigits {string}: optional, number format option
  • maximumFractionDigits {string}: optional, number format option
  • minimumSignificantDigits {string}: optional, number format option
  • maximumSignificantDigits {string}: optional, number format option
  • localeMatcher {string}: optional, number format option
  • formatMatcher {string}: optional, number format option

Any specified number format options will have priority over numberFormats of VueI18n constructor.

Tip

Note that you need to guarantee this context equal to component instance in lifecycle methods (e.g. in data options, const $n = this.$n.bind(this)).

# Injected properties

# $i18n

  • Type: I18n

  • Read only

Get a VueI18n instance. If you are specify.

If you have specified an i18n option at component options, you will be able to get a VueI18n instance at the component, Otherwise, you will be able get root VueI18n instance.

# VueI18n class

VueI18n class implement I18n interface of flowtype definitions

# Static properties

# version

  • Type: string

vue-i18n version.

# availabilities

🆕 7.0+

  • Type: IntlAvailability

Whether the following internationalization features are available:

  • {boolean} dateTimeFormat: locale sensitive datetime formatting

  • {boolean} numberFormat: locale sensitive number formatting

The above internationalization features are depends on the browser environments, due to implement with ECMAScript Internationalization API (ECMA-402).

# Constructor options

You can specify the below some options of I18nOptions constructor options of flowtype definitions

# locale

  • Type: Locale

  • Default: 'en-US'

The locale of localization. If the locale contains a territory and a dialect, this locale contains an implicit fallback.

# fallbackLocale

  • Type: FallbackLocale

  • Default: false

The locale of fallback localization. For more complex fallback definitions see fallback.

# messages

  • Type: LocaleMessages

  • Default: {}

The locale messages of localization.

# dateTimeFormats

🆕 7.0+

  • Type: DateTimeFormats

  • Default: {}

The datetime formats of localization.

# numberFormats

🆕 7.0+

  • Type: NumberFormats

  • Default: {}

The number formats of localization.

# availableLocales

🆕 8.9.0+

  • Type: Locale[]

  • Default: []

  • Examples: ["en", "ja"]

The list of available locales in messages in lexical order.

# formatter

  • Type: Formatter

  • Default: Built in formatter

The formatter that implemented with Formatter interface.

# modifiers

🆕 8.15.0+

  • Type: Modifier

  • Default: lower and upper modifiers

Modifiers functions for linked messages

# missing

  • Type: MissingHandler

  • Default: null

A handler for localization missing. The handler gets called with the localization target locale, localization path key, the Vue instance and values.

If missing handler is assigned, and occurred localization missing, it's not warned.

# fallbackRoot

  • Type: Boolean

  • Default: true

In the component localization, whether to fall back to root level (global) localization when localization fails.

If false, it's warned, and is returned the key.

# sync

  • Type: Boolean

  • Default: true

Whether synchronize the root level locale to the component localization locale.

If false, regardless of the root level locale, localize for each component locale.

# silentTranslationWarn

6.1+, 🆙 8.13

  • Type: Boolean | RegExp

  • Default: false

Whether suppress warnings outputted when localization fails.

If true, suppress localization fail warnings. If you use regular expression, you can suppress localization fail warnings that it match with translation key (e.g. $t).

# silentFallbackWarn

🆕 8.8+, 🆙 8.13

  • Type: Boolean | RegExp
  • Default: false

Whether suppress warnings when falling back to either fallbackLocale or root.

If true, warnings will be generated only when no translation is available at all, and not for fallbacks. If you use regular expression, you can suppress the fallback warnings that it match key (e.g. $t).

# preserveDirectiveContent

8.7+

  • Type: Boolean

  • Default: false

Whether v-t directive's element should preserve textContent after directive is unbinded.

# warnHtmlInMessage

8.11+

  • Type: WarnHtmlInMessageLevel

  • Default: off

Whether to allow the use locale messages of HTML formatting. See the warnHtmlInMessage property.

Important!!

In next major version, warnHtmlInMessage option is warn as default.

# sharedMessages

8.12+

  • Type: LocaleMessages

  • Default: undefined

The shared locale messages of localization for components. More detail see Component based localization.

# postTranslation

8.16+

  • Type: PostTranslationHandler

  • Default: null

A handler for post processing of translation. The handler gets after being called with the $t, t, $tc, and tc.

This handler is useful if you want to filter on translated text such as space trimming.

# Properties

# locale

  • Type: Locale

  • Read/Write

The locale of localization. If the locale contains a territory and a dialect, this locale contains an implicit fallback.

# fallbackLocale

  • Type: FallbackLocale

  • Read/Write

The locale of fallback localization. For more complex fallback definitions see fallback.

# messages

  • Type: LocaleMessages

  • Read only

The locale messages of localization.

# dateTimeFormats

🆕 7.0+

  • Type: DateTimeFormats

  • Read only

The datetime formats of localization.

# numberFormats

🆕 7.0+

  • Type: NumberFormats

  • Read only

The number formats of localization.

# missing

  • Type: MissingHandler

  • Read/Write

A handler for localization missing.

# formatter

  • Type: Formatter

  • Read/Write

The formatter that implemented with Formatter interface.

# silentTranslationWarn

6.1+, 🆙 8.13

  • Type: Boolean | RegExp

  • Read/Write

Whether suppress warnings outputted when localization fails.

# silentFallbackWarn

🆕 8.8+, 🆙 8.13

  • Type: Boolean | RegExp

  • Read/Write

Whether suppress fallback warnings when localization fails.

# preserveDirectiveContent

8.7+

  • Type: Boolean

  • Read/Write

Whether v-t directive's element should preserve textContent after directive is unbinded.

# warnHtmlInMessage

8.11+

  • Type: WarnHtmlInMessageLevel

  • Read/Write

Whether to allow the use locale messages of HTML formatting.

If you set warn orerror, will check the locale messages on the VueI18n instance.

If you are specified warn, a warning will be output at console. If you are specified error will occurred an Error.

In VueI18n instance, set the off as default.

# postTranslation

8.16+

  • Type: PostTranslationHandler

  • Read/Write

A handler for post processing of translation.

# Methods

# getChoiceIndex

  • Arguments:

    • {number} choice
    • {number} choicesLength
  • Return: finalChoice {number}

Get pluralization index for current pluralizing number and a given amount of choices. Can be overridden through prototype mutation:

VueI18n.prototype.getChoiceIndex = /* custom implementation */

# getLocaleMessage( locale )

  • Arguments:

    • {Locale} locale
  • Return: LocaleMessageObject

Get the locale message of locale.

# setLocaleMessage( locale, message )

  • Arguments:

    • {Locale} locale
    • {LocaleMessageObject} message

Set the locale message of locale.

NOTE

8.11+

If you set warn orerror in the warnHtmlInMessage property, when this method is executed, it will check if HTML formatting is used for locale message.

# mergeLocaleMessage( locale, message )

6.1+

  • Arguments:

    • {Locale} locale
    • {LocaleMessageObject} message

Merge the registered locale messages with the locale message of locale.

NOTE

8.11+

If you set warn orerror in the warnHtmlInMessage property, when this method is executed, it will check if HTML formatting is used for locale message.

# t( key, [locale], [values] )

  • Arguments:

    • {Path} key: required
    • {Locale} locale: optional
    • {Array | Object} values: optional
  • Return: : TranslateResult

This is the same as the Function returned with $t method. More detail see $t.

# tc( key, [choice], [values] )

  • Arguments:

    • {Path} key: required
    • {number} choice: optional, default 1
    • {string | Array | Object} values: optional
  • Return: TranslateResult

This is the same as the Function returned $tc method. More detail see $tc.

# te( key, [locale] )

  • Arguments:

    • {string} key: required
    • {Locale} locale: optional
  • Return: boolean

Check whether key path exists in global locale message. If you specified locale, check the locale message of locale.

# getDateTimeFormat ( locale )

🆕 7.0+

  • Arguments:

    • {Locale} locale
  • Return: DateTimeFormat

Get the datetime format of locale.

# setDateTimeFormat ( locale, format )

🆕 7.0+

  • Arguments:

    • {Locale} locale
    • {DateTimeFormat} format

Set the datetime format of locale.

# mergeDateTimeFormat ( locale, format )

🆕 7.0+

  • Arguments:

    • {Locale} locale
    • {DateTimeFormat} format

Merge the registered datetime formats with the datetime format of locale.

# d( value, [key], [locale] )

🆕 7.0+

  • Arguments:

    • {number | Date} value: required
    • {Path | Object} key: optional
    • {Locale | Object} locale: optional
  • Return: DateTimeFormatResult

This is the same as $d method of Vue instance method. More detail see $d.

# getNumberFormat ( locale )

🆕 7.0+

  • Arguments:

    • {Locale} locale
  • Return: NumberFormat

Get the number format of locale.

# setNumberFormat ( locale, format )

🆕 7.0+

  • Arguments:

    • {Locale} locale
    • {NumberFormat} format

Set the number format of locale.

# mergeNumberFormat ( locale, format )

🆕 7.0+

  • Arguments:

    • {Locale} locale
    • {NumberFormat} format

Merge the registered number formats with the number format of locale.

# n( value, [format], [locale] )

🆕 7.0+

  • Arguments:

    • {number} value: required
    • {Path | Object} format: optional
    • {Locale} locale: optional
  • Return: NumberFormatResult

This is the same as $n method of Vue instance method. More detail see $n.

# Directives

🆕 7.3+

# v-t

  • Expects: string | Object

  • Modifiers:

    • .preserve: (8.7.0+) preserves element textContent when directive is unbinded.
  • Details:

Update the element textContent that localized with locale messages. You can use string syntax or object syntax. string syntax can be specified as a keypath of locale messages. If you can be used object syntax, you need to specify as the object key the following params:

* path: required, key of locale messages
* locale: optional, locale
* args: optional, for list or named formatting

NOTE

The element textContent will be cleared by default when v-t directive is unbinded. This might be undesirable situation when used inside transitions. To preserve textContent data after directive unbind use .preserve modifier or global preserveDirectiveContent option.

  • Examples:
<!-- string syntax: literal -->
<p v-t="'foo.bar'"></p>

<!-- string syntax: binding via data or computed props -->
<p v-t="msg"></p>

<!-- object syntax: literal -->
<p v-t="{ path: 'hi', locale: 'ja', args: { name: 'kazupon' } }"></p>

<!-- object syntax: binding via data or computed props -->
<p v-t="{ path: greeting, args: { name: fullName } }"></p>

<!-- with preserve modifier -->
<p v-t.preserve="'foo.bar'"></p>

# Components

# i18n functional component

🆕 7.0+

# Props:

  • path {Path}: required, keypath of locale messages
  • locale {Locale}: optional, locale
  • tag {string|boolean}: optional, default 'span'
  • places {Array | Object}: optional (7.2+)

Important!!

In next major version, places prop is deprecated. Please switch to slots syntax.

# Usage:

<div id="app">
  <!-- ... -->
  <i18n path="term" tag="label" for="tos">
    <a :href="url" target="_blank">{{ $t('tos') }}</a>
  </i18n>
  <!-- ... -->
</div>
const messages = {
  en: {
    tos: 'Term of Service',
    term: 'I accept xxx {0}.'
  },
  ja: {
    tos: '利用規約',
    term: '私は xxx の{0}に同意します。'
  }
}

const i18n = new VueI18n({
  locale: 'en',
  messages
})
new Vue({
  i18n,
  data: {
    url: '/term'
  }
}).$mount('#app')

# See also:

Component interpolation

# i18n-n functional component

🆕 8.10+

# Props:

  • value {number}: required, number to format
  • format {string | NumberFormatOptions}: optional, number format name or object with explicit format options
  • locale {Locale}: optional, locale
  • tag {string|boolean}: optional, default 'span'

# Usage:

<div id="app">
  <!-- ... -->
  <i18n-n :value="money" format="currency" tag="label">
    <span v-slot:currency="slotProps" class="font-weight: bold">{{ slotProps.currency }}<span>
  </i18n-n>
  <!-- ... -->
</div>
var numberFormats = {
  'en-US': {
    currency: {
      style: 'currency', currency: 'USD'
    }
  },
  'ja-JP': {
    currency: {
      style: 'currency', currency: 'JPY'
    }
  }
}

const i18n = new VueI18n({
  locale: 'en-US',
  numberFormats
})
new Vue({
  i18n,
  data: {
    money: 10234,
  }
}).$mount('#app')

# Scoped slots

<i18n-n> functional component can accept a number of named scoped slots. List of supported slot names is based on Intl.NumberFormat.formatToParts() output types:

  • currency
  • decimal
  • fraction
  • group
  • infinity
  • integer
  • literal
  • minusSign
  • nan
  • plusSign
  • percentSign

Each of these named scoped slots will accept three scope parameters:

  • [slotName] {FormattedNumberPartType}: parameter of the same name as actual slot name (like integer)
  • index {Number}: index of the specific part in the array of number parts
  • parts {Array}: array of all formatted number parts

# See also:

Number custom formatting

# Special Attributes

# place

🆕 7.2+

# Expects: {number | string}

Used on component interpolation to indicate which index of list formatting or key of named formatting.

For detailed usage, see the guide section linked below.

# See also:

Component interpolation

Last Updated: 5/14/2020, 11:30:34 PM
================================================ FILE: docs/assets/css/0.styles.c299b601.css ================================================ .home{padding:3.6rem 2rem 0;max-width:960px;margin:0 auto}.home .hero{text-align:center}.home .hero img{max-width:100%;max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.8rem auto}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:#6a8bad}.home .hero .action-button{display:inline-block;font-size:1.2rem;color:#fff;background-color:#3eaf7c;padding:.8rem 1.6rem;border-radius:4px;transition:background-color .1s ease;box-sizing:border-box;border-bottom:1px solid #389d70}.home .hero .action-button:hover{background-color:#4abf8a}.home .features{border-top:1px solid #eaecef;padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:#3a5169}.home .feature p{color:#4e6e8e}.home .footer{padding:2.5rem;border-top:1px solid #eaecef;text-align:center;color:#4e6e8e}@media (max-width:719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width:419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.sidebar-button{display:none;width:1.25rem;height:1.25rem;position:absolute;padding:.6rem;top:.6rem;left:1rem;cursor:pointer}.sidebar-button .icon{display:block;width:1.25rem;height:1.25rem}@media (max-width:719px){.sidebar-button{display:block}}.search-box{display:inline-block;position:relative;margin-right:1rem}.search-box input{cursor:text;width:10rem;color:#4e6e8e;display:inline-block;border:1px solid #cfd4db;border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:all .2s ease;background:#fff url(/vue-i18n/assets/img/search.83621669.svg) .6rem .5rem no-repeat;background-size:1rem}.search-box input:focus{cursor:auto;border-color:#3eaf7c}.search-box .suggestions{background:#fff;width:20rem;position:absolute;top:1.5rem;border:1px solid #cfd4db;border-radius:6px;padding:.4rem;list-style-type:none}.search-box .suggestions.align-right{right:0}.search-box .suggestion{line-height:1.4;padding:.4rem .6rem;border-radius:4px;cursor:pointer}.search-box .suggestion a{white-space:normal;color:#5d82a6}.search-box .suggestion a .page-title{font-weight:600}.search-box .suggestion a .header{font-size:.9em;margin-left:.25em}.search-box .suggestion.focused{background-color:#f3f4f5}.search-box .suggestion.focused a{color:#3eaf7c}@media (max-width:959px){.search-box input{cursor:pointer;width:0;border-color:transparent;position:relative}.search-box input:focus{cursor:text;left:0;width:10rem}}@media (max-width:959px) and (min-width:719px){.search-box .suggestions{left:0}}@media (max-width:719px){.search-box{margin-right:0}.search-box input{left:1rem}.search-box .suggestions{right:0}}@media (max-width:419px){.search-box .suggestions{width:calc(100vw - 4rem)}.search-box input:focus{width:8rem}}.dropdown-enter,.dropdown-leave-to{height:0!important}.dropdown-wrapper{cursor:pointer}.dropdown-wrapper .dropdown-title{display:block}.dropdown-wrapper .dropdown-title:hover{border-color:transparent}.dropdown-wrapper .dropdown-title .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.dropdown-wrapper .nav-dropdown .dropdown-item{color:inherit;line-height:1.7rem}.dropdown-wrapper .nav-dropdown .dropdown-item h4{margin:.45rem 0 0;border-top:1px solid #eee;padding:.45rem 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper{padding:0;list-style:none}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper .dropdown-subitem{font-size:.9em}.dropdown-wrapper .nav-dropdown .dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active,.dropdown-wrapper .nav-dropdown .dropdown-item a:hover{color:#3eaf7c}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{content:"";width:0;height:0;border-left:5px solid #3eaf7c;border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.dropdown-wrapper .nav-dropdown .dropdown-item:first-child h4{margin-top:0;padding-top:0;border-top:0}@media (max-width:719px){.dropdown-wrapper.open .dropdown-title{margin-bottom:.5rem}.dropdown-wrapper .nav-dropdown{transition:height .1s ease-out;overflow:hidden}.dropdown-wrapper .nav-dropdown .dropdown-item h4{border-top:0;margin-top:0;padding-top:0}.dropdown-wrapper .nav-dropdown .dropdown-item>a,.dropdown-wrapper .nav-dropdown .dropdown-item h4{font-size:15px;line-height:2rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem{font-size:14px;padding-left:1rem}}@media (min-width:719px){.dropdown-wrapper{height:1.8rem}.dropdown-wrapper:hover .nav-dropdown{display:block!important}.dropdown-wrapper .dropdown-title .arrow{border-left:4px solid transparent;border-right:4px solid transparent;border-top:6px solid #ccc;border-bottom:0}.dropdown-wrapper .nav-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:#fff;padding:.6rem 0;border:1px solid #ddd;border-bottom-color:#ccc;text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}}.nav-links{display:inline-block}.nav-links a{line-height:1.4rem;color:inherit}.nav-links a.router-link-active,.nav-links a:hover{color:#3eaf7c}.nav-links .nav-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:2rem}.nav-links .nav-item:first-child{margin-left:0}.nav-links .repo-link{margin-left:1.5rem}@media (max-width:719px){.nav-links .nav-item,.nav-links .repo-link{margin-left:0}}@media (min-width:719px){.nav-links a.router-link-active,.nav-links a:hover{color:#2c3e50}.nav-item>a:not(.external).router-link-active,.nav-item>a:not(.external):hover{margin-bottom:-2px;border-bottom:2px solid #46bd87}}.navbar{padding:.7rem 1.5rem;line-height:2.2rem;position:relative}.navbar a,.navbar img,.navbar span{display:inline-block}.navbar .logo{height:2.2rem;min-width:2.2rem;margin-right:.8rem;vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:#2c3e50;position:relative}.navbar .links{padding-left:1.5rem;box-sizing:border-box;background-color:#fff;white-space:nowrap;font-size:.9rem;position:absolute;right:1.5rem;top:.7rem;display:flex}.navbar .links .search-box{flex:0 0 auto;vertical-align:top}.navbar .links .nav-links{flex:1}@media (max-width:719px){.navbar{padding-left:4rem}.navbar .can-hide{display:none}.navbar .links{padding-left:1.5rem}}.page-edit,.page-nav{max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.page-edit,.page-nav{padding:2rem}}@media (max-width:419px){.page-edit,.page-nav{padding:1.5rem}}.page{padding-bottom:2rem}.page-edit{padding-top:1rem;padding-bottom:1rem;overflow:auto}.page-edit .edit-link{display:inline-block}.page-edit .edit-link a{color:#4e6e8e;margin-right:.25rem}.page-edit .last-updated{float:right;font-size:.9em}.page-edit .last-updated .prefix{font-weight:500;color:#4e6e8e}.page-edit .last-updated .time{font-weight:400;color:#aaa}.page-nav{padding-top:1rem;padding-bottom:0}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid #eaecef;padding-top:1rem;overflow:auto}.page-nav .next{float:right}@media (max-width:719px){.page-edit .edit-link{margin-bottom:.5rem}.page-edit .last-updated{font-size:.8em;float:none;text-align:left}}.sidebar .sidebar-sub-headers{padding-left:1rem;font-size:.95em}a.sidebar-link{font-weight:400;display:inline-block;color:#2c3e50;border-left:.25rem solid transparent;padding:.35rem 1rem .35rem 1.25rem;line-height:1.4;width:100%;box-sizing:border-box}a.sidebar-link:hover{color:#3eaf7c}a.sidebar-link.active{font-weight:600;color:#3eaf7c;border-left-color:#3eaf7c}.sidebar-group a.sidebar-link{padding-left:2rem}.sidebar-sub-headers a.sidebar-link{padding-top:.25rem;padding-bottom:.25rem;border-left:none}.sidebar-sub-headers a.sidebar-link.active{font-weight:500}.sidebar-group:not(.first){margin-top:1em}.sidebar-group .sidebar-group{padding-left:.5em}.sidebar-group:not(.collapsable) .sidebar-heading{cursor:auto;color:inherit}.sidebar-heading{color:#999;transition:color .15s ease;cursor:pointer;font-size:1.1em;font-weight:700;padding:0 1.5rem;margin-top:0;margin-bottom:.5rem}.sidebar-heading.open,.sidebar-heading:hover{color:inherit}.sidebar-heading .arrow{position:relative;top:-.12em;left:.5em}.sidebar-heading:.open .arrow{top:-.18em}.sidebar-group-items{transition:height .1s ease-out;overflow:hidden}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .nav-links{display:none;border-bottom:1px solid #eaecef;padding:.5rem 0 .75rem 0}.sidebar .nav-links a{font-weight:600}.sidebar .nav-links .nav-item,.sidebar .nav-links .repo-link{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar .sidebar-links{padding:1.5rem 0}@media (max-width:719px){.sidebar .nav-links{display:block}.sidebar .nav-links .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{top:calc(1rem - 2px)}.sidebar .sidebar-links{padding:1rem 0}}.sw-update-popup{position:fixed;right:1em;bottom:1em;padding:1em;border:1px solid #3eaf7c;border-radius:3px;background:#fff;box-shadow:0 4px 16px rgba(0,0,0,.5);text-align:center}.sw-update-popup button{margin-top:.5em;padding:.25em 2em}.sw-update-popup-enter-active,.sw-update-popup-leave-active{transition:opacity .3s,transform .3s}.sw-update-popup-enter,.sw-update-popup-leave-to{opacity:0;transform:translateY(50%) scale(.5)}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}#nprogress{pointer-events:none}#nprogress .bar{background:#3eaf7c;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #3eaf7c,0 0 5px #3eaf7c;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#3eaf7c;border-left-color:#3eaf7c;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.content code{color:#476582;padding:.25rem .5rem;margin:0;font-size:.85em;background-color:rgba(27,31,35,.05);border-radius:3px}.content pre,.content pre[class*=language-]{line-height:1.4;padding:1.25rem 1.5rem;margin:.85rem 0;background-color:#282c34;border-radius:6px;overflow:auto}.content pre[class*=language-] code,.content pre code{color:#fff;padding:0;background-color:transparent;border-radius:0}div[class*=language-]{position:relative;background-color:#282c34;border-radius:6px}div[class*=language-] .highlight-lines{-webkit-user-select:none;-ms-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.4}div[class*=language-] .highlight-lines .highlighted{background-color:rgba(0,0,0,.66)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent;position:relative;z-index:1}div[class*=language-]:before{position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:hsla(0,0%,100%,.4)}div[class*=language-]:not(.line-numbers-mode) .line-numbers-wrapper{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlighted{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlighted:before{content:" ";position:absolute;z-index:3;left:0;top:0;display:block;width:3.5rem;height:100%;background-color:rgba(0,0,0,.66)}div[class*=language-].line-numbers-mode pre{padding-left:4.5rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers-wrapper{position:absolute;top:0;width:3.5rem;text-align:center;color:hsla(0,0%,100%,.3);padding:1.25rem 0;line-height:1.4}div[class*=language-].line-numbers-mode .line-numbers-wrapper br{-webkit-user-select:none;-ms-user-select:none;user-select:none}div[class*=language-].line-numbers-mode .line-numbers-wrapper .line-number{position:relative;z-index:4;-webkit-user-select:none;-ms-user-select:none;user-select:none;font-size:.85em}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;z-index:2;top:0;left:0;width:3.5rem;height:100%;border-radius:6px 0 0 6px;border-right:1px solid rgba(0,0,0,.66);background-color:#282c34}div[class~=language-js]:before{content:"js"}div[class~=language-ts]:before{content:"ts"}div[class~=language-html]:before{content:"html"}div[class~=language-md]:before{content:"md"}div[class~=language-vue]:before{content:"vue"}div[class~=language-css]:before{content:"css"}div[class~=language-sass]:before{content:"sass"}div[class~=language-scss]:before{content:"scss"}div[class~=language-less]:before{content:"less"}div[class~=language-stylus]:before{content:"stylus"}div[class~=language-go]:before{content:"go"}div[class~=language-java]:before{content:"java"}div[class~=language-c]:before{content:"c"}div[class~=language-sh]:before{content:"sh"}div[class~=language-yaml]:before{content:"yaml"}div[class~=language-py]:before{content:"py"}div[class~=language-javascript]:before{content:"js"}div[class~=language-typescript]:before{content:"ts"}div[class~=language-markup]:before{content:"html"}div[class~=language-markdown]:before{content:"md"}div[class~=language-json]:before{content:"json"}div[class~=language-ruby]:before{content:"rb"}div[class~=language-python]:before{content:"py"}div[class~=language-bash]:before{content:"sh"}.custom-block .custom-block-title{font-weight:600;margin-bottom:-.4rem}.custom-block.danger,.custom-block.tip,.custom-block.warning{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-block.tip{background-color:#f3f5f7;border-color:#42b983}.custom-block.warning{background-color:rgba(255,229,100,.3);border-color:#e7c000;color:#6b5900}.custom-block.warning .custom-block-title{color:#b29400}.custom-block.warning a{color:#2c3e50}.custom-block.danger{background-color:#ffe6e6;border-color:#c00;color:#4d0000}.custom-block.danger .custom-block-title{color:#900}.custom-block.danger a{color:#2c3e50}.arrow{display:inline-block;width:0;height:0}.arrow.up{border-bottom:6px solid #ccc}.arrow.down,.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent}.arrow.down{border-top:6px solid #ccc}.arrow.right{border-left:6px solid #ccc}.arrow.left,.arrow.right{border-top:4px solid transparent;border-bottom:4px solid transparent}.arrow.left{border-right:6px solid #ccc}.content:not(.custom){max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.content:not(.custom){padding:2rem}}@media (max-width:419px){.content:not(.custom){padding:1.5rem}}.table-of-contents .badge{vertical-align:middle}body,html{padding:0;margin:0}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:16px;color:#2c3e50}.page{padding-left:20rem}.navbar{z-index:20;right:0;height:3.6rem;background-color:#fff;box-sizing:border-box;border-bottom:1px solid #eaecef}.navbar,.sidebar-mask{position:fixed;top:0;left:0}.sidebar-mask{z-index:9;width:100vw;height:100vh;display:none}.sidebar{font-size:15px;background-color:#fff;width:20rem;position:fixed;z-index:10;margin:0;top:3.6rem;left:0;bottom:0;box-sizing:border-box;border-right:1px solid #eaecef;overflow-y:auto}.content:not(.custom)>:first-child{margin-top:3.6rem}.content:not(.custom) a:hover{text-decoration:underline}.content:not(.custom) p.demo{padding:1rem 1.5rem;border:1px solid #ddd;border-radius:4px}.content:not(.custom) img{max-width:100%}.content.custom{padding:0;margin:0}.content.custom img{max-width:100%}a{font-weight:500;text-decoration:none}a,p a code{color:#3eaf7c}p a code{font-weight:400}kbd{background:#eee;border:.15rem solid #ddd;border-bottom:.25rem solid #ddd;border-radius:.15rem;padding:0 .15em}blockquote{font-size:1.2rem;color:#999;border-left:.25rem solid #dfe2e5;margin-left:0;padding-left:1rem}ol,ul{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25}.content:not(.custom)>h1,.content:not(.custom)>h2,.content:not(.custom)>h3,.content:not(.custom)>h4,.content:not(.custom)>h5,.content:not(.custom)>h6{margin-top:-3.1rem;padding-top:4.6rem;margin-bottom:0}.content:not(.custom)>h1:first-child,.content:not(.custom)>h2:first-child,.content:not(.custom)>h3:first-child,.content:not(.custom)>h4:first-child,.content:not(.custom)>h5:first-child,.content:not(.custom)>h6:first-child{margin-top:-1.5rem;margin-bottom:1rem}.content:not(.custom)>h1:first-child+.custom-block,.content:not(.custom)>h1:first-child+p,.content:not(.custom)>h1:first-child+pre,.content:not(.custom)>h2:first-child+.custom-block,.content:not(.custom)>h2:first-child+p,.content:not(.custom)>h2:first-child+pre,.content:not(.custom)>h3:first-child+.custom-block,.content:not(.custom)>h3:first-child+p,.content:not(.custom)>h3:first-child+pre,.content:not(.custom)>h4:first-child+.custom-block,.content:not(.custom)>h4:first-child+p,.content:not(.custom)>h4:first-child+pre,.content:not(.custom)>h5:first-child+.custom-block,.content:not(.custom)>h5:first-child+p,.content:not(.custom)>h5:first-child+pre,.content:not(.custom)>h6:first-child+.custom-block,.content:not(.custom)>h6:first-child+p,.content:not(.custom)>h6:first-child+pre{margin-top:2rem}h1:hover .header-anchor,h2:hover .header-anchor,h3:hover .header-anchor,h4:hover .header-anchor,h5:hover .header-anchor,h6:hover .header-anchor{opacity:1}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid #eaecef}h3{font-size:1.35rem}a.header-anchor{font-size:.85em;float:left;margin-left:-.87em;padding-right:.23em;margin-top:.125em;opacity:0}a.header-anchor:hover{text-decoration:none}.line-number,code,kbd{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}ol,p,ul{line-height:1.7}hr{border:0;border-top:1px solid #eaecef}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto}tr{border-top:1px solid #dfe2e5}tr:nth-child(2n){background-color:#f6f8fa}td,th{border:1px solid #dfe2e5;padding:.6em 1em}.custom-layout{padding-top:3.6rem}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.no-navbar .content:not(.custom)>h1,.theme-container.no-navbar h2,.theme-container.no-navbar h3,.theme-container.no-navbar h4,.theme-container.no-navbar h5,.theme-container.no-navbar h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .sidebar{top:0}.theme-container.no-navbar .custom-layout{padding-top:0}@media (min-width:720px){.theme-container.no-sidebar .sidebar{display:none}.theme-container.no-sidebar .page{padding-left:0}}@media (max-width:959px){.sidebar{font-size:15px;width:16.4rem}.page{padding-left:16.4rem}}@media (max-width:719px){.sidebar{top:0;padding-top:3.6rem;transform:translateX(-100%);transition:transform .2s ease}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translateX(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width:419px){h1{font-size:1.9rem}.content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.icon.outbound{color:#aaa;display:inline-block}.badge[data-v-099ab69c]{display:inline-block;font-size:14px;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:#fff;margin-right:5px;background-color:#42b983}.badge.middle[data-v-099ab69c]{vertical-align:middle}.badge.top[data-v-099ab69c]{vertical-align:top}.badge.green[data-v-099ab69c],.badge.tip[data-v-099ab69c]{background-color:#42b983}.badge.error[data-v-099ab69c]{background-color:#da5961}.badge.warn[data-v-099ab69c],.badge.warning[data-v-099ab69c],.badge.yellow[data-v-099ab69c]{background-color:#e7c000} ================================================ FILE: docs/assets/js/10.13689381.js ================================================ (window.webpackJsonp=window.webpackJsonp||[]).push([[10],{242:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),a("p",[t._v("Locale messages:")]),t._v(" "),t._m(2),a("p",[t._v("Template:")]),t._v(" "),t._m(3),a("p",[t._v("Output:")]),t._v(" "),t._m(4),t._m(5),t._v(" "),a("p",[t._v("Locale messages:")]),t._v(" "),t._m(6),a("p",[t._v("Template:")]),t._v(" "),t._m(7),a("p",[t._v("Output:")]),t._v(" "),t._m(8),a("p",[t._v("List formatting also accepts array-like objects:")]),t._v(" "),t._m(9),a("p",[t._v("Output:")]),t._v(" "),t._m(10),t._m(11),t._v(" "),a("div",{staticClass:"warning custom-block"},[a("p",{staticClass:"custom-block-title"},[t._v("Notice")]),t._v(" "),a("p",[t._v("⚠️ Dynamically localizing arbitrary HTML on your website can be very dangerous because it can easily lead to XSS vulnerabilities. Only use HTML interpolation on trusted content and never on user-provided content.")]),t._v(" "),a("p",[t._v("We recommended using the "),a("router-link",{attrs:{to:"./interpolation.html"}},[t._v("component interpolation")]),t._v(" feature.")],1)]),t._v(" "),t._m(12),t._v(" "),a("p",[t._v("In some cases you might want to render your translation as an HTML message and not a static string.")]),t._v(" "),t._m(13),a("p",[t._v("Template:")]),t._v(" "),t._m(14),a("p",[t._v("Output (instead of the pre-formatted message )")]),t._v(" "),t._m(15),t._m(16),t._v(" "),a("p",[t._v("Locale messages:")]),t._v(" "),t._m(17),a("p",[t._v("Template:")]),t._v(" "),t._m(18),a("p",[t._v("Output:")]),t._v(" "),t._m(19),t._m(20),t._v(" "),a("p",[t._v("Sometimes, you may need to translate using custom formatting (e.g. "),a("a",{attrs:{href:"http://userguide.icu-project.org/formatparse/messages",target:"_blank",rel:"noopener noreferrer"}},[t._v("ICU message syntax"),a("OutboundLink")],1),t._v(").")]),t._v(" "),a("p",[t._v("You can do that with a custom formatter that implements the "),a("a",{attrs:{href:"https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js#L41-L43",target:"_blank",rel:"noopener noreferrer"}},[t._v("Formatter Interface"),a("OutboundLink")],1),t._v(".")]),t._v(" "),a("p",[t._v("The following custom formatter with ES2015 class syntax:")]),t._v(" "),t._m(21),a("p",[t._v("You can check "),a("a",{attrs:{href:"https://github.com/kazupon/vue-i18n/tree/dev/examples/formatting/custom",target:"_blank",rel:"noopener noreferrer"}},[t._v("the custom formatter official example"),a("OutboundLink")],1),t._v(".")])])}),[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"formatting"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#formatting"}},[this._v("#")]),this._v(" Formatting")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"named-formatting"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#named-formatting"}},[this._v("#")]),this._v(" Named formatting")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" messages "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n en"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n message"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n hello"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'{msg} world'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('message.hello', { msg: 'hello' }) }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("hello world"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"list-formatting"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#list-formatting"}},[this._v("#")]),this._v(" List formatting")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" messages "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n en"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n message"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n hello"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'{0} world'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('message.hello', ['hello']) }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("hello world"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('message.hello', {'0': 'hello'}) }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("hello world"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"html-formatting"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#html-formatting"}},[this._v("#")]),this._v(" HTML formatting")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"warning custom-block"},[s("p",{staticClass:"custom-block-title"},[this._v("Notice")]),this._v(" "),s("blockquote",[s("p",[this._v("🆕 8.11+")])]),this._v(" "),s("p",[this._v("You can control the use of HTML formatting. see the detail "),s("code",[this._v("warnHtmlInMessage")]),this._v(" constructor option and property API.")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" messages "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n en"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n message"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n hello"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'hello
world'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("v-html")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("$t('message.hello')"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("hello\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!--
exists but is rendered as html and not a string--\x3e")]),t._v("\nworld"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"support-ruby-on-rails-i18n-format"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#support-ruby-on-rails-i18n-format"}},[this._v("#")]),this._v(" Support ruby on rails i18n format")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" messages "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n en"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n message"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n hello"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'%{msg} world'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('message.hello', { msg: 'hello' }) }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("hello world"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"custom-formatting"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#custom-formatting"}},[this._v("#")]),this._v(" Custom formatting")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Custom Formatter implementation")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CustomFormatter")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("constructor")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("options")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// interpolate")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// @param {string} message")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// string of list or named format.")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// e.g.")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// - named formatting: 'Hi {name}'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// - list formatting: 'Hi {0}'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// @param {Object | Array} values")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// values of `message` interpolation.")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// passed values with `$t`, `$tc` and `i18n` functional component.")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// e.g.")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// - $t('hello', { name: 'kazupon' }) -> passed values: Object `{ name: 'kazupon' }`")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// - $t('hello', ['kazupon']) -> passed values: Array `['kazupon']`")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// - `i18n` functional component (component interpolation)")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v('// ')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//

kazupon

")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//

how are you?

")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//
")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// -> passed values: Array (included VNode):")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// `[VNode{ tag: 'p', text: 'kazupon', ...}, VNode{ tag: 'p', text: 'how are you?', ...}]`")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// @return {Array}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// interpolated values. you need to return the following:")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// - array of string, when is using `$t` or `$tc`.")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// - array included VNode object, when is using `i18n` functional component.")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("interpolate")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("message"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" values")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// implement interpolation logic here")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// return the interpolated array")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'resolved message string'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// register with `formatter` option")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" i18n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VueI18n")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n locale"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'en-US'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n formatter"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("CustomFormatter")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/* here the constructor options */")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n messages"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'en-US'")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Run!")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Vue")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" i18n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("$mount")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'#app'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])}],!1,null,null,null);s.default=e.exports}}]); ================================================ FILE: docs/assets/js/11.aa13f293.js ================================================ (window.webpackJsonp=window.webpackJsonp||[]).push([[11],{243:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("p",[t._v("You can use Webpack's "),a("a",{attrs:{href:"https://webpack.js.org/concepts/hot-module-replacement/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Hot Module Replacement"),a("OutboundLink")],1),t._v(" (HMR) feature to watch for changes in localization files and hot reload changes into your application.")]),t._v(" "),t._m(1),t._v(" "),a("p",[t._v("If you only use a static set of locales, you can hot reload those locales explicitly:")]),t._v(" "),t._m(2),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5)])}),[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"hot-reloading"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#hot-reloading"}},[this._v("#")]),this._v(" Hot reloading")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"basic-example"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#basic-example"}},[this._v("#")]),this._v(" Basic example")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" Vue "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"vue"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" VueI18n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"vue-i18n"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" en "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'./en'")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" ja "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'./ja'")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" messages "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n en"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n ja\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// VueI18n instance")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" i18n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VueI18n")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n locale"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'en'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n messages\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Run app")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" app "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Vue")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i18n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("$mount")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'#app'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Hot updates")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("module"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("hot"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n module"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("hot"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("accept")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'./en'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'./ja'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i18n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setLocaleMessage")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'en'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("require")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'./en'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("default"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n i18n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setLocaleMessage")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'ja'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("require")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'./ja'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("default"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Or the following hot updates via $i18n property")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// app.$i18n.setLocaleMessage('en', require('./en').default)")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// app.$i18n.setLocaleMessage('ja', require('./ja').default)")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"advanced-example"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#advanced-example"}},[this._v("#")]),this._v(" Advanced example")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("If you want to support a changing set of locales, you can hot reload those locales dynamically using "),s("code",[this._v("require.context")]),this._v(":")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" Vue "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"vue"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" VueI18n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"vue-i18n"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\nVue"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("use")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("VueI18n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Load all locales and remember context")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("loadMessages")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" context "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" require"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("context")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"./locales"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token regex"}},[t._v("/[a-z0-9-_]+\\.json$/i")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" messages "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" context\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("keys")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("map")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("key")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" locale"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("match")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token regex"}},[t._v("/[a-z0-9-_]+/i")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("reduce")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("messages"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" locale "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("messages"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("locale"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("context")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("key"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" messages "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" messages "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("loadMessages")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// VueI18n instance")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" i18n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VueI18n")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n locale"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"en"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n messages"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Run app")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" app "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Vue")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i18n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("$mount")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'#app'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Hot updates")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("module"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("hot"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n module"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("hot"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("accept")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("id"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" messages"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" newMessages "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("loadMessages")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n Object"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("keys")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("newMessages"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("filter")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("locale")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" messages"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("locale"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!==")]),t._v(" newMessages"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("locale"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("forEach")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("locale")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n messages"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("locale"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" newMessages"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("locale"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n i18n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setLocaleMessage")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("locale"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" messages"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("locale"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])}],!1,null,null,null);s.default=e.exports}}]); ================================================ FILE: docs/assets/js/12.2b21ead8.js ================================================ (window.webpackJsonp=window.webpackJsonp||[]).push([[12],{244:function(t,a,s){"use strict";s.r(a);var n=s(0),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),s("p",[t._v("Sometimes, we need to localize with a locale message that was included in a HTML tag or component. For example:")]),t._v(" "),t._m(3),t._m(4),t._v(" "),t._m(5),s("p",[t._v("And your localized template may look like this:")]),t._v(" "),t._m(6),s("p",[t._v("Output:")]),t._v(" "),t._m(7),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._m(11),s("p",[t._v("the following output:")]),t._v(" "),t._m(12),s("p",[t._v("About the above example, see the "),s("a",{attrs:{href:"https://github.com/kazupon/vue-i18n/tree/dev/examples/interpolation/places",target:"_blank",rel:"noopener noreferrer"}},[t._v("example"),s("OutboundLink")],1)]),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),s("p",[t._v("It's more convenient to use the named slots syntax. For example:")]),t._v(" "),t._m(20),t._m(21),s("p",[t._v("Outputs:")]),t._v(" "),t._m(22),s("p",[t._v("In Vue 2.6 and later, you can use the following slots syntax in templates:")]),t._v(" "),t._m(23),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._m(31),s("p",[t._v("Outputs:")]),t._v(" "),t._m(32),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35),s("p",[t._v("Outputs:")]),t._v(" "),t._m(36)])}),[function(){var t=this.$createElement,a=this._self._c||t;return a("h1",{attrs:{id:"component-interpolation"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#component-interpolation"}},[this._v("#")]),this._v(" Component interpolation")])},function(){var t=this.$createElement,a=this._self._c||t;return a("h2",{attrs:{id:"basic-usage"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#basic-usage"}},[this._v("#")]),this._v(" Basic Usage")])},function(){var t=this.$createElement,a=this._self._c||t;return a("div",{staticClass:"tip custom-block"},[a("p",{staticClass:"custom-block-title"},[this._v("Support Version")]),this._v(" "),a("p",[this._v("🆕 7.0+")])])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("I accept xxx "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("a")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("href")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("/term"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("Terms of Service Agreement"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,a=this._self._c||t;return a("p",[this._v("In the above message, if you use "),a("code",[this._v("$t")]),this._v(", you will probably try to compose the following locale messages:")])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" messages "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n en"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n term1"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'I Accept xxx\\'s'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n term2"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Terms of Service Agreement'")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('term1') }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("a")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("href")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("/term"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('term2') }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("I accept xxx "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("a")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("href")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("/term"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("Terms of Service Agreement"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,a=this._self._c||t;return a("p",[this._v("This is very cumbersome, and if you configure the "),a("code",[this._v("")]),this._v(" tag in a locale message, there is a possibility of XSS vulnerabilities due to localizing with\n"),a("code",[this._v("v-html=\"$t('term')\"")]),this._v(".")])},function(){var t=this.$createElement,a=this._self._c||t;return a("p",[this._v("You can avoid it using the "),a("code",[this._v("i18n")]),this._v(" functional component. For example:")])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("i18n")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("path")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("term"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("tag")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("label"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("for")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("tos"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("a")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":href")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("url"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("target")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("_blank"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('tos') }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" messages "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n en"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n tos"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Term of Service'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n term"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'I accept xxx {0}.'")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n ja"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n tos"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'利用規約'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n term"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'私は xxx の{0}に同意します。'")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" i18n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VueI18n")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n locale"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'en'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n messages\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Vue")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i18n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n data"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n url"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/term'")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("$mount")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'#app'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("label")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("for")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("tos"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n I accept xxx "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("a")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("href")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("/term"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("target")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("_blank"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("Term of Service"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v(".\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,a=this._self._c||t;return a("p",[this._v("The children of "),a("code",[this._v("i18n")]),this._v(" functional component are interpolated with locale message of "),a("code",[this._v("path")]),this._v(" prop. In the above example,")])},function(){var t=this.$createElement,a=this._self._c||t;return a("div",{pre:!0},[a("p",[a("code",[this._v('{{ $t(\'tos\') }}')])])])},function(){var t=this.$createElement,a=this._self._c||t;return a("p",[this._v("is interpolated with "),a("code",[this._v("term")]),this._v(" locale message.")])},function(){var t=this.$createElement,a=this._self._c||t;return a("p",[this._v("In the above example, the component interpolation follows the "),a("strong",[this._v("list formatting")]),this._v(". The children of "),a("code",[this._v("i18n")]),this._v(" functional component are interpolated by their order of appearance.")])},function(){var t=this.$createElement,a=this._self._c||t;return a("p",[this._v("You can choose the root container's node type by specifying a "),a("code",[this._v("tag")]),this._v(" prop. If omitted, it defaults to "),a("code",[this._v("'span'")]),this._v(". You can also set it to the boolean value "),a("code",[this._v("false")]),this._v(" to insert the child nodes directly without creating a root element.")])},function(){var t=this.$createElement,a=this._self._c||t;return a("h2",{attrs:{id:"slots-syntax-usage"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#slots-syntax-usage"}},[this._v("#")]),this._v(" Slots syntax usage")])},function(){var t=this.$createElement,a=this._self._c||t;return a("div",{staticClass:"tip custom-block"},[a("p",{staticClass:"custom-block-title"},[this._v("Support Version")]),this._v(" "),a("p",[this._v("🆕 8.14+")])])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("i18n")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("path")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("info"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("tag")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("p"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("template")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("v-slot:")]),t._v("limit")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ changeLimit }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("template")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("v-slot:")]),t._v("action")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("a")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":href")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("changeUrl"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('change') }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" messages "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n en"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n info"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'You can {action} until {limit} minutes from departure.'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n change"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'change your flight'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n refund"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'refund the ticket'")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" i18n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VueI18n")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n locale"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'en'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n messages\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Vue")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i18n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n data"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n changeUrl"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/change'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n refundUrl"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/refund'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n changeLimit"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("15")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n refundLimit"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("30")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("$mount")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'#app'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n You can "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("a")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("href")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("/change"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("change your flight"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v(" until "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("15"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v(" minutes from departure.\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("i18n")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("path")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("info"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("tag")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("p"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("template")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("#limit")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ changeLimit }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("template")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("#action")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("a")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":href")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("changeUrl"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('change') }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,a=this._self._c||t;return a("div",{staticClass:"warning custom-block"},[a("p",{staticClass:"custom-block-title"},[this._v("Limitation")]),this._v(" "),a("p",[this._v("⚠️ In "),a("code",[this._v("i18n")]),this._v(" component, slots props are not supported.")])])},function(){var t=this.$createElement,a=this._self._c||t;return a("h2",{attrs:{id:"places-syntax-usage"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#places-syntax-usage"}},[this._v("#")]),this._v(" Places syntax usage")])},function(){var t=this.$createElement,a=this._self._c||t;return a("div",{staticClass:"danger custom-block"},[a("p",{staticClass:"custom-block-title"},[this._v("Important!!")]),this._v(" "),a("p",[this._v("In the next major version, the "),a("code",[this._v("place")]),this._v(" and "),a("code",[this._v("places")]),this._v(" props will be deprecated. Please switch to slots syntax.")])])},function(){var t=this.$createElement,a=this._self._c||t;return a("div",{staticClass:"tip custom-block"},[a("p",{staticClass:"custom-block-title"},[this._v("Support Version")]),this._v(" "),a("p",[this._v("🆕 7.2+")])])},function(){var t=this.$createElement,a=this._self._c||t;return a("div",{staticClass:"warning custom-block"},[a("p",{staticClass:"custom-block-title"},[this._v("Notice")]),this._v(" "),a("p",[this._v("⚠️ In "),a("code",[this._v("i18n")]),this._v(" component, text content consisting of only white spaces will be omitted.")])])},function(){var t=this.$createElement,a=this._self._c||t;return a("p",[this._v("Named formatting is supported with the help of "),a("code",[this._v("place")]),this._v(" attribute. For example:")])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("i18n")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("path")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("info"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("tag")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("p"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("place")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("limit"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ changeLimit }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("a")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("place")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("action"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":href")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("changeUrl"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('change') }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" messages "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n en"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n info"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'You can {action} until {limit} minutes from departure.'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n change"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'change your flight'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n refund"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'refund the ticket'")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" i18n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VueI18n")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n locale"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'en'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n messages\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Vue")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i18n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n data"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n changeUrl"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/change'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n refundUrl"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'/refund'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n changeLimit"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("15")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n refundLimit"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("30")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("$mount")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'#app'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n You can "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("a")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("href")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("/change"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("change your flight"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v(" until "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("15"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v(" minutes from departure.\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,a=this._self._c||t;return a("div",{staticClass:"warning custom-block"},[a("p",{staticClass:"custom-block-title"},[this._v("Notice")]),this._v(" "),a("p",[this._v("⚠️ To use named formatting, all children of "),a("code",[this._v("i18n")]),this._v(" component must have "),a("code",[this._v("place")]),this._v(" attribute set. Otherwise it will fallback to list formatting.")])])},function(){var t=this.$createElement,a=this._self._c||t;return a("p",[this._v("If you still want to interpolate text content in named formatting, you could define "),a("code",[this._v("places")]),this._v(" property on "),a("code",[this._v("i18n")]),this._v(" component. For example:")])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("i18n")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("path")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("info"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("tag")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("p"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":places")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("{ limit: refundLimit }"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("a")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("place")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("action"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":href")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("refundUrl"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('refund') }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n You can "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("a")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("href")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("/refund"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("refund your ticket"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v(" until 30 minutes from departure.\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- ... --\x3e")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])}],!1,null,null,null);a.default=e.exports}}]); ================================================ FILE: docs/assets/js/13.b3706367.js ================================================ (window.webpackJsonp=window.webpackJsonp||[]).push([[13],{245:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("p",[t._v("Loading all of your translation files at once is overkill and unnecessary.")]),t._v(" "),a("p",[t._v("Lazy loading or asynchronously loading the translation files is really easy when using Webpack.")]),t._v(" "),a("p",[t._v("Let´s assume we have a project directory similar to the one below:")]),t._v(" "),t._m(1),t._m(2),t._v(" "),t._m(3),t._m(4),t._v(" "),t._m(5),t._v(" "),a("p",[t._v("You can learn more about the import function in the "),a("a",{attrs:{href:"https://webpack.js.org/guides/code-splitting/#dynamic-imports",target:"_blank",rel:"noopener noreferrer"}},[t._v("Webpack documentation"),a("OutboundLink")],1),t._v(".")]),t._v(" "),t._m(6),t._v(" "),t._m(7),t._m(8)])}),[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"lazy-loading-translations"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#lazy-loading-translations"}},[this._v("#")]),this._v(" Lazy loading translations")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("our-cool-project\n-dist\n-src\n--routes\n--store\n--setup\n---i18n-setup.js\n--lang\n---en.js\n---it.js\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("The "),s("code",[this._v("lang")]),this._v(" folder is where all of our translation files reside. The "),s("code",[this._v("setup")]),this._v(" folder is where our arbitrary setup files like the i18n-setup, global component inits, plugin inits and other reside.")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//i18n-setup.js")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" Vue "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'vue'")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" VueI18n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'vue-i18n'")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" messages "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'@/lang/en'")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" axios "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'axios'")]),t._v("\n\nVue"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("use")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("VueI18n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("export")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" i18n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VueI18n")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n locale"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'en'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// set locale")]),t._v("\n fallbackLocale"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'en'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n messages "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// set locale messages")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" loadedLanguages "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'en'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// our default language that is preloaded")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setI18nLanguage")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("lang")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i18n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("locale "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" lang\n axios"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("defaults"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("headers"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("common"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Accept-Language'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" lang\n document"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("querySelector")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'html'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setAttribute")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'lang'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" lang"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" lang\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("export")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("loadLanguageAsync")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("lang")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// If the same language")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i18n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("locale "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("===")]),t._v(" lang"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" Promise"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("resolve")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setI18nLanguage")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("lang"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// If the language was already loaded")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("loadedLanguages"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("includes")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("lang"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" Promise"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("resolve")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setI18nLanguage")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("lang"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// If the language hasn't been loaded yet")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v('/* webpackChunkName: "lang-[request]" */')]),t._v(" "),a("span",{pre:!0,attrs:{class:"token template-string"}},[a("span",{pre:!0,attrs:{class:"token template-punctuation string"}},[t._v("`")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("@/i18n/messages/")]),a("span",{pre:!0,attrs:{class:"token interpolation"}},[a("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[t._v("${")]),t._v("lang"),a("span",{pre:!0,attrs:{class:"token interpolation-punctuation punctuation"}},[t._v("}")])]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v(".js")]),a("span",{pre:!0,attrs:{class:"token template-punctuation string"}},[t._v("`")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("then")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("messages")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i18n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setLocaleMessage")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("lang"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" messages"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("default"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n loadedLanguages"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("push")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("lang"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("setI18nLanguage")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("lang"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("In short we are creating a new VueI18n instance as we normally would. Then we are creating a "),s("code",[this._v("loadedLanguages")]),this._v(" array that will keep track of our loaded languages. Next is the "),s("code",[this._v("setI18nLanguage")]),this._v(" function that will actually change the language in our vueI18n instance, axios and where ever else is needed.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("The "),s("code",[this._v("loadLanguageAsync")]),this._v(" function is what we will actually use to change the languages. Loading the new files is done via the "),s("code",[this._v("import")]),this._v(" function, which is generously provided by Webpack and it allows us to load files dynamically, and because it uses promises we can easily wait for the loading to finish.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Using the "),s("code",[this._v("loadLanguageAsync")]),this._v(" function is straightforward. A common use case is inside a vue-router beforeEach hook.")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("router"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("beforeEach")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("to"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("from")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" next")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" lang "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" to"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("params"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("lang\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("loadLanguageAsync")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("lang"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("then")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("next")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("We could improve this by checking if the "),s("code",[this._v("lang")]),this._v(" is actually supported by us or not, call "),s("code",[this._v("reject")]),this._v(" so we can catch that in the "),s("code",[this._v("beforeEach")]),this._v(" stopping the route transition.")])}],!1,null,null,null);s.default=e.exports}}]); ================================================ FILE: docs/assets/js/14.1819508a.js ================================================ (window.webpackJsonp=window.webpackJsonp||[]).push([[14],{246:function(t,a,s){"use strict";s.r(a);var n=s(0),e=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._m(4),t._v(" "),s("p",[t._v("Example:")]),t._v(" "),t._m(5),t._m(6),t._v(" "),s("div",{staticClass:"warning custom-block"},[s("p",{staticClass:"custom-block-title"},[t._v("Component vs. root scope")]),t._v(" "),s("p",[t._v("⚠️ Changing "),s("code",[t._v("$i18n.locale")]),t._v(" inside a component does not update the root locale.\nIf you rely on the root locale, for example when using "),s("router-link",{attrs:{to:"./fallback.html"}},[t._v("root fallbacks")]),t._v(", use "),s("code",[t._v("$root.$i18n.locale")]),t._v(" instead of "),s("code",[t._v("$i18n.locale")]),t._v(".")],1)])])}),[function(){var t=this.$createElement,a=this._self._c||t;return a("h1",{attrs:{id:"locale-changing"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#locale-changing"}},[this._v("#")]),this._v(" Locale changing")])},function(){var t=this.$createElement,a=this._self._c||t;return a("p",[this._v("Normally, using the root Vue instance as the starting point, all child components are localized using the "),a("code",[this._v("locale")]),this._v(" property of the "),a("code",[this._v("VueI18n")]),this._v(" class as a reference.")])},function(){var t=this.$createElement,a=this._self._c||t;return a("p",[this._v("Sometimes you might want to dynamically change the locale. In that case you can change the value of the "),a("code",[this._v("locale")]),this._v(" property of the "),a("code",[this._v("VueI18n")]),this._v(" instance.")])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" i18n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VueI18n")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n locale"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'ja'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// set locale")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// create root Vue instance")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Vue")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i18n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("$mount")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'#app'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// change other locale")]),t._v("\ni18n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("locale "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'en'")]),t._v("\n")])])])},function(){var t=this.$createElement,a=this._self._c||t;return a("p",[this._v("Each component contains a "),a("code",[this._v("VueI18n")]),this._v(" instance referenced as the "),a("code",[this._v("$i18n")]),this._v(" property that can also be used to change the locale.")])},function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("template")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("class")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("locale-changer"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("select")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("v-model")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("$i18n.locale"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("option")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("v-for")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("(lang, i) in langs"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":key")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("`Lang${i}`"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":value")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("lang"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ lang }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("script")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),s("span",{pre:!0,attrs:{class:"token script"}},[s("span",{pre:!0,attrs:{class:"token language-javascript"}},[t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("export")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("default")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'locale-changer'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("data")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" langs"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'ja'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'en'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])]),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,a=this._self._c||t;return a("div",{staticClass:"warning custom-block"},[a("p",{staticClass:"custom-block-title"},[this._v("Notice")]),this._v(" "),a("p",[this._v("⚠️ Locale changing is ignored for components that use "),a("code",[this._v("sync: false")]),this._v(".")])])}],!1,null,null,null);a.default=e.exports}}]); ================================================ FILE: docs/assets/js/15.12152ec7.js ================================================ (window.webpackJsonp=window.webpackJsonp||[]).push([[15],{247:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},(function(){var t=this.$createElement;this._self._c;return this._m(0)}),[function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[a("h1",{attrs:{id:"locale-messages-syntax"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#locale-messages-syntax"}},[t._v("#")]),t._v(" Locale messages syntax")]),t._v(" "),a("h2",{attrs:{id:"structure"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#structure"}},[t._v("#")]),t._v(" Structure")]),t._v(" "),a("p",[t._v("Locale Messages syntax below:")]),t._v(" "),a("div",{staticClass:"language-typescript extra-class"},[a("pre",{pre:!0,attrs:{class:"language-typescript"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// As Flowtype definition, Locale Messages syntax like BNF annotation")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" LocaleMessages "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("key"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Locale"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" LocaleMessageObject "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" LocaleMessageObject "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("key"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" Path"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" LocaleMessage "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" LocaleMessageArray "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" LocaleMessage"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" LocaleMessage "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" LocaleMessageObject "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" LocaleMessageArray"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Locale "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Path "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])]),a("p",[t._v("Based on the above syntax, You can configure the following Locale Messages structure:")]),t._v(" "),a("div",{staticClass:"language-json extra-class"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"en"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 'en' Locale")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"key1"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"this is message1"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// basic")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"nested"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// nested")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"message1"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"this is nested message1"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"errors"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// array")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"this is 0 error code message"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// object in array")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"internal1"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"this is internal 1 error message"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// array in array")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"this is nested array error 1"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"ja"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 'ja' Locale")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("In the above Locale Messages structure, You can translate using below key paths.")]),t._v(" "),a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- basic --\x3e")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('key1') }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- nested --\x3e")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('nested.message1') }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- array --\x3e")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('errors[0]') }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- object in array --\x3e")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('errors[1].internal1') }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- array in array --\x3e")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('errors[2][0]') }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])]),a("p",[t._v("Output the following:")]),t._v(" "),a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- basic --\x3e")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("this is message1"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- nested --\x3e")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("this is nested message1"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- array --\x3e")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("this is 0 error code message"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- object in array --\x3e")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("this is internal 1 error message"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- array in array --\x3e")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("this is nested array error 1"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])]),a("h2",{attrs:{id:"linked-locale-messages"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#linked-locale-messages"}},[t._v("#")]),t._v(" Linked locale messages")]),t._v(" "),a("p",[t._v("If there's a translation key that will always have the same concrete text as another one you can just link to it. To link to another translation key, all you have to do is to prefix its contents with an "),a("code",[t._v("@:")]),t._v(" sign followed by the full name of the translation key including the namespace you want to link to.")]),t._v(" "),a("p",[t._v("Locale messages the below:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" messages "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n en"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n message"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n the_world"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'the world'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n dio"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'DIO:'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n linked"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'@:message.dio @:message.the_world !!!!'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("Template:")]),t._v(" "),a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('message.linked') }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])]),a("p",[t._v("Output:")]),t._v(" "),a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("DIO: the world !!!!"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])]),a("h3",{attrs:{id:"formatting-linked-locale-messages"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#formatting-linked-locale-messages"}},[t._v("#")]),t._v(" Formatting linked locale messages")]),t._v(" "),a("p",[t._v("If the language distinguish cases of character, you may need control the case of the linked locale messages.\nLinked messages can be formatted with modifier "),a("code",[t._v("@.modifier:key")])]),t._v(" "),a("p",[t._v("The below modifiers are available currently.")]),t._v(" "),a("ul",[a("li",[a("code",[t._v("upper")]),t._v(": Uppercase all characters in the linked message.")]),t._v(" "),a("li",[a("code",[t._v("lower")]),t._v(": Lowercase all characters in the linked message.")]),t._v(" "),a("li",[a("code",[t._v("capitalize")]),t._v(": Capitalize the first character in the linked message.")])]),t._v(" "),a("p",[t._v("Locale messages the below:")]),t._v(" "),a("div",{staticClass:"language-javascript extra-class"},[a("pre",{pre:!0,attrs:{class:"language-javascript"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" messages "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n en"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n message"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n homeAddress"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Home address'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n missingHomeAddress"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'Please provide @.lower:message.homeAddress'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("label")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('message.homeAddress') }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("class")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("error"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('message.missingHomeAddress') }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])]),a("p",[t._v("Output the below:")]),t._v(" "),a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("label")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("Home address"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("class")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("error"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("Please provide home address"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])]),a("p",[t._v("You can add modifiers or overwrite the existing ones passing the "),a("code",[t._v("modifiers")]),t._v(" options to the "),a("code",[t._v("VueI18n")]),t._v(" constructor.")]),t._v(" "),a("div",{staticClass:"language-javascript extra-class"},[a("pre",{pre:!0,attrs:{class:"language-javascript"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" i18n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VueI18n")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n locale"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'en'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n messages"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n modifiers"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function-variable function"}},[t._v("snakeCase")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("str")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" str"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("split")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("' '")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("join")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'-'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("h3",{attrs:{id:"grouping-by-brackets"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#grouping-by-brackets"}},[t._v("#")]),t._v(" Grouping by brackets")]),t._v(" "),a("p",[t._v("A translation key of linked locale message can also have the form of "),a("code",[t._v("@:(message.foo.bar.baz)")]),t._v(" in which the link to another translation key is within brackets "),a("code",[t._v("()")]),t._v(".")]),t._v(" "),a("p",[t._v("This can be useful if the link "),a("code",[t._v("@:message.something")]),t._v(" is followed by period "),a("code",[t._v(".")]),t._v(", which otherwise would be part of the link and may not need to be.")]),t._v(" "),a("p",[t._v("Locale messages:")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" messages "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n en"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n message"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n dio"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'DIO'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n linked"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'There\\'s a reason, you lost, @:(message.dio).'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("Template:")]),t._v(" "),a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $t('message.linked') }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])]),a("p",[t._v("Output:")]),t._v(" "),a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("There's a reason, you lost, DIO."),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])])}],!1,null,null,null);s.default=e.exports}}]); ================================================ FILE: docs/assets/js/16.c251473e.js ================================================ (window.webpackJsonp=window.webpackJsonp||[]).push([[16],{248:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),a("p",[t._v("You can localize the number with your definition formats.")]),t._v(" "),a("p",[t._v("Number formats the below:")]),t._v(" "),t._m(2),a("p",[t._v("As the above, you can define named number formats (e.g. "),a("code",[t._v("currency")]),t._v(", etc), and you need to use "),a("a",{attrs:{href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat",target:"_blank",rel:"noopener noreferrer"}},[t._v("the options with ECMA-402 Intl.NumberFormat"),a("OutboundLink")],1)]),t._v(" "),t._m(3),t._v(" "),t._m(4),a("p",[t._v("Template the below:")]),t._v(" "),t._m(5),a("p",[t._v("Output the below:")]),t._v(" "),t._m(6),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),a("p",[t._v("The following template:")]),t._v(" "),t._m(11),a("p",[t._v("will produce the below output:")]),t._v(" "),t._m(12),a("p",[t._v("But the real power of this component comes into play when it is used with "),a("a",{attrs:{href:"https://vuejs.org/v2/guide/components-slots.html#Scoped-Slots",target:"_blank",rel:"noopener noreferrer"}},[t._v("scoped slots"),a("OutboundLink")],1),t._v(".")]),t._v(" "),t._m(13),t._v(" "),t._m(14),a("p",[t._v("Template above will result in the following HTML:")]),t._v(" "),t._m(15),a("p",[t._v("It is possible to specify multiple scoped slots at the same time:")]),t._v(" "),t._m(16),a("p",[t._v("(this resulting HTML was formatted for better readability)")]),t._v(" "),t._m(17),t._m(18),t._v(" "),a("p",[t._v("Full list of the supported scoped slots as well as other "),a("code",[t._v("")]),t._v(" properties can be found "),a("router-link",{attrs:{to:"./../api/#i18n-n-functional-component"}},[t._v("on API page")]),t._v(".")],1)])}),[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"number-localization"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#number-localization"}},[this._v("#")]),this._v(" Number localization")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"tip custom-block"},[s("p",{staticClass:"custom-block-title"},[this._v("Support Version")]),this._v(" "),s("p",[this._v("🆕 7.0+")])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" numberFormats "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'en-US'")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n currency"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n style"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'currency'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" currency"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'USD'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'ja-JP'")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n currency"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n style"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'currency'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" currency"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'JPY'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" currencyDisplay"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'symbol'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("After that, when using the locale messages, you need to specify the "),s("code",[this._v("numberFormats")]),this._v(" option of the "),s("code",[this._v("VueI18n")]),this._v(" constructor:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" i18n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VueI18n")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n numberFormats\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Vue")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i18n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("$mount")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'#app'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $n(100, 'currency') }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $n(100, 'currency', 'ja-JP') }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("$100.00"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("¥100"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"custom-formatting"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#custom-formatting"}},[this._v("#")]),this._v(" Custom formatting")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"tip custom-block"},[s("p",{staticClass:"custom-block-title"},[this._v("Support Version")]),this._v(" "),s("p",[this._v("🆕 8.10+")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("code",[this._v("$n")]),this._v(" method returns resulting string with fully formatted number, which can only be used as a whole. In situations when you need to style some part of the formatted number (like fraction digits), "),s("code",[this._v("$n")]),this._v(" is not enough. In such cases "),s("code",[this._v("")]),this._v(" functional component will be of help.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("With a minimum set of properties, "),s("code",[this._v("")]),this._v(" generates the same output as "),s("code",[this._v("$n")]),this._v(", wrapped into configured DOM element.")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("i18n-n")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":value")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("100"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("i18n-n")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":value")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("100"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("format")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("currency"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("i18n-n")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":value")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("100"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("format")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("currency"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("locale")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("ja-JP"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("id")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("app"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("100"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("$100.00"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("¥100"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("Let's say there is a requirement to render the integer part of the number with a bolder font. This can be achieved by specifying "),s("code",[this._v("integer")]),this._v(" scoped slot element:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("i18n-n")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":value")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("100"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("format")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("currency"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("v-slot:")]),t._v("integer")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("slotProps"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("styles")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("font-weight: bold"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ slotProps.integer }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("$"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("styles")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("font-weight: bold"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("100"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v(".00"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("i18n-n")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":value")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("1234"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":format")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("{ key: 'currency', currency: 'EUR' }"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("v-slot:")]),t._v("currency")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("slotProps"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("styles")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("color: green"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ slotProps.currency }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("v-slot:")]),t._v("integer")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("slotProps"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("styles")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("font-weight: bold"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ slotProps.integer }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("v-slot:")]),t._v("group")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("slotProps"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("styles")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("font-weight: bold"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ slotProps.group }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[a("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("v-slot:")]),t._v("fraction")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("slotProps"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("styles")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("font-size: small"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ slotProps.fraction }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("styles")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("color: green"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("€"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("styles")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("font-weight: bold"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("1"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("styles")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("font-weight: bold"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v(","),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("styles")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("font-weight: bold"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("234"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n .\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("styles")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("font-size: small"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("00"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("You can choose the root container's node type by specifying a "),s("code",[this._v("tag")]),this._v(" prop. If omitted, it defaults to "),s("code",[this._v("'span'")]),this._v(". You can also set it to the boolean value "),s("code",[this._v("false")]),this._v(" to insert the child nodes directly without creating a root element.")])}],!1,null,null,null);s.default=e.exports}}]); ================================================ FILE: docs/assets/js/17.311179bd.js ================================================ (window.webpackJsonp=window.webpackJsonp||[]).push([[17],{249:function(t,a,s){"use strict";s.r(a);var n=s(0),p=Object(n.a)({},(function(){var t=this.$createElement;this._self._c;return this._m(0)}),[function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("div",{staticClass:"content"},[s("h1",{attrs:{id:"pluralization"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#pluralization"}},[t._v("#")]),t._v(" Pluralization")]),t._v(" "),s("p",[t._v("You can translate with pluralization. You must define the locale that have a pipe "),s("code",[t._v("|")]),t._v(" separator, and define plurals in pipe separator.")]),t._v(" "),s("p",[s("em",[t._v("Your template will need to use "),s("code",[t._v("$tc()")]),t._v(" instead of "),s("code",[t._v("$t()")]),t._v(".")])]),t._v(" "),s("p",[t._v("Locale messages below:")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" messages "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n en"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n car"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'car | cars'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n apple"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'no apples | one apple | {count} apples'")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("Template below:")]),t._v(" "),s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('car', 1) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('car', 2) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('apple', 0) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('apple', 1) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('apple', 10, { count: 10 }) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])]),s("p",[t._v("Output below:")]),t._v(" "),s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("car"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("cars"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("no apples"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("one apple"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("10 apples"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])]),s("h2",{attrs:{id:"accessing-the-number-via-the-pre-defined-argument"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#accessing-the-number-via-the-pre-defined-argument"}},[t._v("#")]),t._v(" Accessing the number via the pre-defined argument")]),t._v(" "),s("p",[t._v("You don't need to explicitly give the number for pluralization.\nThe number can be accessed within locale messages via pre-defined named arguments "),s("code",[t._v("{count}")]),t._v(" and/or "),s("code",[t._v("{n}")]),t._v(".\nYou can overwrite those pre-defined named arguments if necessary.")]),t._v(" "),s("p",[t._v("Locale messages the below:")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" messages "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n en"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n apple"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'no apples | one apple | {count} apples'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n banana"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'no bananas | {n} banana | {n} bananas'")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("Template below:")]),t._v(" "),s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('apple', 10, { count: 10 }) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('apple', 10) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('banana', 1, { n: 1 }) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('banana', 1) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('banana', 100, { n: 'too many' }) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])]),s("p",[t._v("Output below:")]),t._v(" "),s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("10 apples"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("10 apples"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("1 banana"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("1 banana"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("too many bananas"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])]),s("h2",{attrs:{id:"custom-pluralization"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#custom-pluralization"}},[t._v("#")]),t._v(" Custom pluralization")]),t._v(" "),s("p",[t._v("Such pluralization, however, does not apply to all languages (Slavic languages, for example, have different pluralization rules).")]),t._v(" "),s("p",[t._v("In order to implement these rules you can override the "),s("code",[t._v("VueI18n.prototype.getChoiceIndex")]),t._v(" function.")]),t._v(" "),s("p",[t._v("Very simplified example using rules for Slavic languages (Russian, Ukrainian, etc.):")]),t._v(" "),s("div",{staticClass:"language-js extra-class"},[s("pre",{pre:!0,attrs:{class:"language-js"}},[s("code",[t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" defaultImpl "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VueI18n")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("prototype"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("getChoiceIndex\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/**\n * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`\n * @param choicesLength {number} an overall amount of available choices\n * @returns a final choice index to select plural word by\n**/")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("VueI18n")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("prototype"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function-variable function"}},[t._v("getChoiceIndex")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("choice"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" choicesLength")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// this === VueI18n instance, so the locale property also exists here")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("this")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("locale "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'ru'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// proceed to the default implementation")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("defaultImpl")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("apply")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("this")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" arguments"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("choice "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("===")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" teen "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" choice "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" choice "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("20")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" endsWithOne "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" choice "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("===")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("teen "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" endsWithOne"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("teen "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" choice "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" choice "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("choicesLength "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("?")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("This would effectively give this:")]),t._v(" "),s("div",{staticClass:"language-javascript extra-class"},[s("pre",{pre:!0,attrs:{class:"language-javascript"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" messages "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ru"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n car"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'0 машин | {n} машина | {n} машины | {n} машин'")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n banana"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v("'нет бананов | {n} банан | {n} банана | {n} бананов'")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("Where the format is "),s("code",[t._v("0 things | 1 thing | few things | multiple things")]),t._v(".")]),t._v(" "),s("p",[t._v("Your template still needs to use "),s("code",[t._v("$tc()")]),t._v(", not "),s("code",[t._v("$t()")]),t._v(":")]),t._v(" "),s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('car', 1) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('car', 2) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('car', 4) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('car', 12) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('car', 21) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('banana', 0) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('banana', 4) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('banana', 11) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ $tc('banana', 31) }}"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])]),s("p",[t._v("Which results in:")]),t._v(" "),s("div",{staticClass:"language-html extra-class"},[s("pre",{pre:!0,attrs:{class:"language-html"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("1 машина"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("2 машины"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("4 машины"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("12 машин"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("21 машина"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("нет бананов"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("4 банана"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("11 бананов"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("31 банан"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])])}],!1,null,null,null);a.default=p.exports}}]); ================================================ FILE: docs/assets/js/18.a9d0f1d4.js ================================================ (window.webpackJsonp=window.webpackJsonp||[]).push([[18],{250:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),a("p",[t._v("The following in "),a("a",{attrs:{href:"https://github.com/kazupon/vue-i18n/tree/dev/examples/sfc",target:"_blank",rel:"noopener noreferrer"}},[t._v("single file components example"),a("OutboundLink")],1),t._v(":")]),t._v(" "),t._m(3),t._m(4),t._v(" "),a("p",[t._v("You need to install "),a("code",[t._v("vue-loader")]),t._v(" and "),a("code",[t._v("vue-i18n-loader")]),t._v(" due to use "),a("code",[t._v("")]),t._v(" custom blocks. While "),a("a",{attrs:{href:"https://github.com/vuejs/vue-loader",target:"_blank",rel:"noopener noreferrer"}},[t._v("vue-loader"),a("OutboundLink")],1),t._v(" most likely is already used in your project if you are working with single file components, you must install "),a("a",{attrs:{href:"https://github.com/kazupon/vue-i18n-loader",target:"_blank",rel:"noopener noreferrer"}},[t._v("vue-i18n-loader"),a("OutboundLink")],1),t._v(" additionally:")]),t._v(" "),t._m(5),t._m(6),t._v(" "),a("p",[t._v("For Webpack the configuration below is required:")]),t._v(" "),a("p",[t._v("for vue-loader v15 or later:")]),t._v(" "),t._m(7),a("p",[t._v("for vue-loader v14:")]),t._v(" "),t._m(8),t._m(9),t._v(" "),a("p",[a("a",{attrs:{href:"https://github.com/vuejs/vue-cli",target:"_blank",rel:"noopener noreferrer"}},[t._v("Vue CLI 3.0"),a("OutboundLink")],1),t._v(" hides the webpack configuration, so, if we want to add support to the "),a("code",[t._v("")]),t._v(" tag inside a single file component we need to modify the existing configuration.")]),t._v(" "),t._m(10),t._v(" "),a("p",[t._v("for vue-loader v15 or later:")]),t._v(" "),t._m(11),a("p",[t._v("for vue-loader v14:")]),t._v(" "),t._m(12),a("p",[a("em",[t._v("Don 't forget to install "),a("a",{attrs:{href:"https://github.com/KyleAMathews/deepmerge",target:"_blank",rel:"noopener noreferrer"}},[t._v("deepmerge"),a("OutboundLink")],1),t._v("! ("),a("code",[t._v("npm i deepmerge -D")]),t._v(" or "),a("code",[t._v("yarn add deepmerge -D")]),t._v(")")])]),t._v(" "),a("p",[t._v("If you want to read more about modifying the existing configuration "),a("a",{attrs:{href:"https://cli.vuejs.org/guide/webpack.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("click here"),a("OutboundLink")],1),t._v(".")]),t._v(" "),t._m(13),t._v(" "),a("p",[t._v("for Laravel-mix 4 with vue-loader v15 or later:")]),t._v(" "),t._m(14),a("p",[t._v("for Laravel-mix 2 with vue-loader v14:")]),t._v(" "),a("p",[t._v("As of "),a("a",{attrs:{href:"https://github.com/JeffreyWay/laravel-mix/releases/tag/v2.1",target:"_blank",rel:"noopener noreferrer"}},[t._v("V2.1"),a("OutboundLink")],1),t._v(" of Laravel-mix, you can add custom rules via mix.extend(). Laravel mix already has its own rules for handling .vue files. To add the "),a("code",[t._v("vue-i18n-loader")]),t._v(", add the following to "),a("code",[t._v("webpack.mix.js")])]),t._v(" "),t._m(15),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),a("p",[t._v("Webpack conf the below:")]),t._v(" "),a("p",[t._v("for vue-loader v15 or later:")]),t._v(" "),t._m(20),a("p",[t._v("for vue-loader v14:")]),t._v(" "),t._m(21),t._m(22),t._v(" "),t._m(23),t._v(" "),t._m(24),t._m(25),t._v(" "),a("p",[t._v("In this way, multiple custom blocks useful when want to be used as module.")]),t._v(" "),t._m(26),t._v(" "),a("p",[t._v("When using "),a("code",[t._v("vue-i18n")]),t._v(" with "),a("code",[t._v("scoped style")]),t._v(", it's important to remember to use a "),a("a",{attrs:{href:"https://vue-loader.vuejs.org/guide/scoped-css.html#child-component-root-elements",target:"_blank",rel:"noopener noreferrer"}},[t._v("deep selector"),a("OutboundLink")],1),t._v(" for styling an element "),t._m(27),t._v(" the translation string. For example:")]),t._v(" "),t._m(28),t._v(" "),t._m(29),t._m(30),t._v(" "),t._m(31),t._m(32),t._v(" "),t._m(33),t._v(" "),t._m(34),t._v(" "),t._m(35)])}),[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"single-file-components"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#single-file-components"}},[this._v("#")]),this._v(" Single file components")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"basic-usage"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#basic-usage"}},[this._v("#")]),this._v(" Basic Usage")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("If you are building Vue component or Vue application using single file components, you can manage the locale messages "),s("code",[this._v("i18n")]),this._v(" custom block.")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("i18n"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"en"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello world!"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"ja"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"こんにちは、世界!"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("i18n"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("template"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("div id"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"app"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("label "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"locale"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("locale"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("label"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("select v"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("model"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"locale"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("option"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("en"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("option"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("option"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("ja"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("option"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("select"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("p"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("message"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("$t")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'hello'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("p"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("div"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("template"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v("script"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("export")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("default")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'app'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("data")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("this")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("$i18n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("locale "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'en'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" locale"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'en'")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n watch"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("locale")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("val")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("this")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("$i18n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("locale "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" val\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("script"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"installing-vue-i18n-loader"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#installing-vue-i18n-loader"}},[this._v("#")]),this._v(" Installing vue-i18n-loader")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-sh extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v("npm i --save-dev @kazupon/vue-i18n-loader\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"webpack"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#webpack"}},[this._v("#")]),this._v(" Webpack")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("module"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("exports "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n module"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n rules"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n test"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token regex"}},[t._v("/\\.vue$/")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n loader"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'vue-loader'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n resourceQuery"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token regex"}},[t._v("/blockType=i18n/")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n type"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'javascript/auto'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n loader"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'@kazupon/vue-i18n-loader'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("module"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("exports "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n module"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n rules"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n test"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token regex"}},[t._v("/\\.vue$/")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n loader"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'vue-loader'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n options"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n loaders"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// you need to specify `i18n` loaders key with `vue-i18n-loader` (https://github.com/kazupon/vue-i18n-loader)")]),t._v("\n i18n"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'@kazupon/vue-i18n-loader'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"vue-cli-3-0"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#vue-cli-3-0"}},[this._v("#")]),this._v(" Vue CLI 3.0")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("In order to do that we have to create a "),s("code",[this._v("vue.config.js")]),this._v(" at the root of our project. Once we have done that, we have to include the following:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("module"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("exports "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function-variable function"}},[t._v("chainWebpack")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("config")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n config"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("module\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("rule")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"i18n"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("resourceQuery")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token regex"}},[t._v("/blockType=i18n/")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("type")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'javascript/auto'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("use")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"i18n"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("loader")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"@kazupon/vue-i18n-loader"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("end")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" merge "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("require")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'deepmerge'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\nmodule"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("exports "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function-variable function"}},[t._v("chainWebpack")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("config")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n config"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("module\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("rule")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'vue'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("use")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'vue-loader'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("tap")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("options")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("merge")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("options"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n loaders"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i18n"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'@kazupon/vue-i18n-loader'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"laravel-mix"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#laravel-mix"}},[this._v("#")]),this._v(" Laravel-Mix")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v('// Extend Mix with the "i18n" method, that loads the vue-i18n-loader')]),t._v("\nmix"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("extend")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'i18n'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("webpackRules")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n resourceQuery"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token regex"}},[t._v("/blockType=i18n/")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n type"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'javascript/auto'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n loader"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'@kazupon/vue-i18n-loader'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Make sure to call the .i18n() (to load the loader) before .js(..., ...)")]),t._v("\nmix"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("i18n")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("js")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'resources/js/App.js'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'public/js/app.js'")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// The below code will inject i18n Kazupon/vue-18-loader as a loader for .vue files.")]),t._v("\nmix"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("extend")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'i18n'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("function")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("webpackConfig"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("args")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n webpackConfig"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("module"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("rules"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("forEach")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("module")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v('// Search for the "vue-loader" component, which handles .vue files.')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" module"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("loader "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'vue-loader'")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Within this module, add the vue-i18n-loader for the i18n tag.")]),t._v("\n module"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("options"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("loaders"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("i18n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'@kazupon/vue-i18n-loader'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Make sure to call .i18n() before .js(..., ...)")]),t._v("\nmix"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("i18n")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("js")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'resources/assets/js/App.js'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'public/js/app.js'")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"yaml-loading"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#yaml-loading"}},[this._v("#")]),this._v(" YAML loading")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("p",[a("code",[t._v("i18n")]),t._v(" custom blocks need to specify "),a("code",[t._v("JSON")]),t._v(" format, also you can use "),a("code",[t._v("YAML")]),t._v(" format by using pre-loader feature of "),a("code",[t._v("vue-loader")]),t._v(".")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("the "),s("code",[this._v("i18n")]),this._v(" custom blocks below of "),s("code",[this._v("YAML")]),this._v(" format:")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("i18n")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v('\nen:\n hello: "hello world!"\nja:\n hello: "こんにちは、世界!"\n'),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Vue CLI 3.0")]),t._v("\nmodule"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("exports "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function-variable function"}},[t._v("chainWebpack")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token parameter"}},[t._v("config")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n config"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("module\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("rule")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"i18n"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("resourceQuery")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token regex"}},[t._v("/blockType=i18n/")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("type")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'javascript/auto'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("use")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"i18n"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("loader")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"@kazupon/vue-i18n-loader"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("end")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("use")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'yaml'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("loader")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'yaml-loader'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("end")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("module"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("exports "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n module"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n rules"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n test"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token regex"}},[t._v("/\\.vue$/")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n loader"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'vue-loader'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n options"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n preLoaders"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i18n"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'yaml-loader'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n loaders"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i18n"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v("'@kazupon/vue-i18n-loader'")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"multiple-custom-blocks"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#multiple-custom-blocks"}},[this._v("#")]),this._v(" Multiple custom blocks")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("You can use locale messages with multiple "),s("code",[this._v("i18n")]),this._v(" custom blocks.")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("i18n")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("src")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("./common/locales.json"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("i18n")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v('\n {\n "en": {\n "hello": "hello world!"\n },\n "ja": {\n "hello": "こんにちは、世界!"\n }\n }\n'),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("In the above, first custom block load the common locale message with "),s("code",[this._v("src")]),this._v(" attribute, second custom block load the locale message that is defined only at single file component. These locale messages will be merged as locale message of component.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"scoped-style"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#scoped-style"}},[this._v("#")]),this._v(" Scoped style")])},function(){var t=this.$createElement,s=this._self._c||t;return s("strong",[s("em",[this._v("inside")])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Translation contains only text")]),this._v(" (Work without deep selector)")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[t._v("...\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("i18n")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v('\n{\n "en": {\n "hello": "hello world!"\n },\n "ja": {\n "hello": "こんにちは、世界"\n }\n}\n'),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n...\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("template")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("class")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("parent"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("message: {{ $t('hello') }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n...\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- Will work --\x3e")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("style")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token style"}},[a("span",{pre:!0,attrs:{class:"token language-css"}},[t._v("\n"),a("span",{pre:!0,attrs:{class:"token selector"}},[t._v(".parent p")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v("color")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" #42b883"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])]),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("Translation with HTML element")]),this._v(" (Must use deep selector)")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[t._v("...\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("i18n")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v('\n{\n "en": {\n "hello": "hello'),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("world!"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v('"\n },\n "ja": {\n "hello": "こんにちは、'),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("span")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("世界!"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v('"\n }\n}\n'),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n...\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("template")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("div")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("class")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("parent"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("v-html")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("$t('hello')"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n...\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- Won't work --\x3e")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("style")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token style"}},[a("span",{pre:!0,attrs:{class:"token language-css"}},[t._v("\n"),a("span",{pre:!0,attrs:{class:"token selector"}},[t._v(".parent p")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v("color")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" #42b883"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token selector"}},[t._v(".parent p span")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v("color")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" red"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])]),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- Will work --\x3e")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("style")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token style"}},[a("span",{pre:!0,attrs:{class:"token language-css"}},[t._v("\n"),a("span",{pre:!0,attrs:{class:"token selector"}},[t._v(".parent p")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v("color")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" #42b883"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token selector"}},[t._v(".parent p >>> span")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v("color")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" red"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])]),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"custom-blocks-in-functional-component"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#custom-blocks-in-functional-component"}},[this._v("#")]),this._v(" Custom blocks in functional component")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("If the single file components have the template using a functional component, and you had been defined "),s("code",[this._v("i18n")]),this._v(" custom blocks, note you cannot localize using locale messages.")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("For example, the following code cannot localize with the locale message of "),s("code",[this._v("i18n")]),this._v(" custom block.")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("i18n")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v('\n{\n "en": {\n "hello": "hello world"\n },\n "ja": {\n "hello": "こんにちは、世界"\n }\n}\n'),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("template")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("functional")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("\x3c!-- 'hello' of locale messages of parent instance --\x3e")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("p")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("{{ parent.$t('hello') }}"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])])}],!1,null,null,null);s.default=e.exports}}]); ================================================ FILE: docs/assets/js/19.d0288f09.js ================================================ (window.webpackJsonp=window.webpackJsonp||[]).push([[19],{251:function(t,e,n){"use strict";n.r(e);var i=n(0),r=Object(i.a)({},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("p",[t._v("To support the i18n of Vue applications, some tools are officially provided.")]),t._v(" "),n("p",[t._v("There are also tools from third vendors integrating Vue I18n.")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),n("p",[n("a",{attrs:{href:"https://github.com/kazupon/vue-cli-plugin-i18n",target:"_blank",rel:"noopener noreferrer"}},[t._v("vue-cli-plugin-i18n"),n("OutboundLink")],1),t._v(" is officially provided as the Vue CLI Plugin.")]),t._v(" "),n("p",[t._v("With this plugin, you can setup the i18n environment for the Vue application, and support the i18n development environment.")]),t._v(" "),t._m(3),t._v(" "),n("p",[n("a",{attrs:{href:"https://github.com/nuxt-community/nuxt-i18n/",target:"_blank",rel:"noopener noreferrer"}},[t._v("nuxt-i18n"),n("OutboundLink")],1),t._v(" is corresponding Nuxt.js module.")]),t._v(" "),t._m(4),t._v(" "),n("p",[n("a",{attrs:{href:"https://github.com/kazupon/vue-i18n-loader",target:"_blank",rel:"noopener noreferrer"}},[t._v("vue-i18n-loader"),n("OutboundLink")],1),t._v(" is an officially provided webpack loader.")]),t._v(" "),t._m(5),t._v(" "),n("p",[t._v("In about "),n("code",[t._v("i18n")]),t._v(" custom block, see the "),n("router-link",{attrs:{to:"./sfc.html"}},[t._v("Single file components section")])],1),t._v(" "),t._m(6),t._v(" "),n("p",[n("a",{attrs:{href:"https://intlify.github.io/eslint-plugin-vue-i18n/",target:"_blank",rel:"noopener noreferrer"}},[t._v("eslint-plugin-vue-i18n"),n("OutboundLink")],1),t._v(" is ESLint plugin for Vue I18n.")]),t._v(" "),n("p",[t._v("It easily integrates some localization lint features to your Vue.js Application.")]),t._v(" "),t._m(7),t._v(" "),n("p",[n("a",{attrs:{href:"https://github.com/kazupon/vue-i18n-extensions",target:"_blank",rel:"noopener noreferrer"}},[t._v("vue-i18n-extensions"),n("OutboundLink")],1),t._v(" provides some extensions for Vue I18n.")]),t._v(" "),n("p",[t._v("You can use this extension to enable SSR and improve i18n performance.")]),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),n("p",[n("a",{attrs:{href:"https://www.codeandweb.com/babeledit",target:"_blank",rel:"noopener noreferrer"}},[t._v("BabelEdit"),n("OutboundLink")],1),t._v(" is translation editor for web apps.")]),t._v(" "),t._m(10),t._v(" "),n("p",[t._v("Read more about BabelEdit in "),n("a",{attrs:{href:"https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-vue-app-with-vue-i18n",target:"_blank",rel:"noopener noreferrer"}},[t._v("tutorial page"),n("OutboundLink")],1),t._v(".")]),t._v(" "),t._m(11),t._v(" "),n("p",[n("a",{attrs:{href:"https://marketplace.visualstudio.com/items?itemName=antfu.i18n-ally",target:"_blank",rel:"noopener noreferrer"}},[t._v("i18n Ally"),n("OutboundLink")],1),t._v(" is i18n extension for VSCode.")]),t._v(" "),n("p",[t._v("i18n Ally give awesome DX for your i18n development.")]),t._v(" "),n("p",[t._v("Read more about i18n Ally in "),n("a",{attrs:{href:"https://github.com/antfu/i18n-ally/blob/master/README.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("README"),n("OutboundLink")],1),t._v(".")]),t._v(" "),t._m(12),t._v(" "),n("p",[n("a",{attrs:{href:"https://github.com/nyavro/i18nPlugin",target:"_blank",rel:"noopener noreferrer"}},[t._v("i18nPlugin"),n("OutboundLink")],1),t._v(" Intellij idea i18next support plugin ( "),n("a",{attrs:{href:"https://plugins.jetbrains.com/plugin/12981-i18n-support",target:"_blank",rel:"noopener noreferrer"}},[t._v("Jetbrains plugin page "),n("OutboundLink")],1),t._v(").")]),t._v(" "),n("p",[t._v('Plugin for i18n typescript/javascript/PHP. Supports vue-i18n. To enable vue-i18n support go to settings -> Tools -> i18n Plugin configuration and check "Vue-i18n". You need set vue locales directory (locales by default).')])])}),[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"tooling"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#tooling"}},[this._v("#")]),this._v(" Tooling")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"official-tooling"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#official-tooling"}},[this._v("#")]),this._v(" Official tooling")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"vue-cli-plugin"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#vue-cli-plugin"}},[this._v("#")]),this._v(" Vue CLI Plugin")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"nuxt-module"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#nuxt-module"}},[this._v("#")]),this._v(" Nuxt Module")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"webpack-loader"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#webpack-loader"}},[this._v("#")]),this._v(" Webpack loader")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("With this loader, you can use the "),e("code",[this._v("i18n")]),this._v(" custom block in the Single file components.")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"eslint-plugin"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#eslint-plugin"}},[this._v("#")]),this._v(" ESLint Plugin")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"extensions"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#extensions"}},[this._v("#")]),this._v(" Extensions")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"_3rd-party-tooling"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_3rd-party-tooling"}},[this._v("#")]),this._v(" 3rd party tooling")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"babeledit"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#babeledit"}},[this._v("#")]),this._v(" BabelEdit")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("BabelEdit can translate "),e("code",[this._v("json")]),this._v(" files, and it can also translate "),e("code",[this._v("i18n")]),this._v(" custom block of Single-file components.")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"i18n-ally"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#i18n-ally"}},[this._v("#")]),this._v(" i18n Ally")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"i18nplugin-intellij-platform"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#i18nplugin-intellij-platform"}},[this._v("#")]),this._v(" i18nPlugin (intellij platform)")])}],!1,null,null,null);e.default=r.exports}}]); ================================================ FILE: docs/assets/js/2.aa910774.js ================================================ (window.webpackJsonp=window.webpackJsonp||[]).push([[2],{165:function(t,n,e){},166:function(t,n,e){"use strict";var a=e(165);e.n(a).a},235:function(t,n,e){"use strict";e.r(n);var a={functional:!0,props:{type:{type:String,default:"tip"},text:String,vertical:{type:String,default:"top"}},render:function(t,n){var e=n.props,a=n.slots;return t("span",{class:["badge",e.type,e.vertical]},e.text||a().default)}},r=(e(166),e(0)),i=Object(r.a)(a,void 0,void 0,!1,null,"099ab69c",null);n.default=i.exports}}]); ================================================ FILE: docs/assets/js/20.9de38e10.js ================================================ (window.webpackJsonp=window.webpackJsonp||[]).push([[20],{252:function(t,e,s){"use strict";s.r(e);var n=s(0),a=Object(n.a)({},(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),s("p",[s("a",{attrs:{href:"https://unpkg.com/vue-i18n/dist/vue-i18n",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://unpkg.com/vue-i18n/dist/vue-i18n"),s("OutboundLink")],1)]),t._v(" "),s("p",[s("a",{attrs:{href:"https://unpkg.com",target:"_blank",rel:"noopener noreferrer"}},[t._v("unpkg.com"),s("OutboundLink")],1),t._v(" provides NPM-based CDN links. The above link will always point to the latest release on NPM. You can also use a specific version/tag via URLs like "),s("a",{attrs:{href:"https://unpkg.com/vue-i18n@8.17.5/dist/vue-i18n.js",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://unpkg.com/vue-i18n@8.17.5/dist/vue-i18n.js"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("Include vue-i18n after Vue and it will install itself automatically:")]),t._v(" "),t._m(4),t._m(5),t._v(" "),t._m(6),t._m(7),t._v(" "),t._m(8),t._m(9),t._v(" "),t._m(10),s("p",[t._v("You don't need to do this when using global script tags.")]),t._v(" "),t._m(11),t._v(" "),t._m(12),s("p",[t._v("You need Vue cli 3.x as pre-requisite, you can install it on your shell with the next command:")]),t._v(" "),t._m(13),t._m(14),t._v(" "),t._m(15),t._v(" "),t._m(16),t._m(17),t._v(" "),s("p",[t._v("In the dist/ "),s("a",{attrs:{href:"https://cdn.jsdelivr.net/npm/vue-i18n/dist/",target:"_blank",rel:"noopener noreferrer"}},[t._v("directory of the NPM package"),s("OutboundLink")],1),t._v(" you will find many different builds of VueI18n. Here’s an overview of the difference between them:")]),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),s("ul",[s("li",[s("p",[s("strong",[s("a",{attrs:{href:"https://github.com/umdjs/umd",target:"_blank",rel:"noopener noreferrer"}},[t._v("UMD"),s("OutboundLink")],1)]),t._v(": UMD builds can be used directly in the browser via a "),s("code",[t._v(" ================================================ FILE: docs/guide/datetime.html ================================================ DateTime localization | Vue I18n

# DateTime localization

Support Version

🆕 7.0+

You can localize the datetime with your definition formats.

DateTime formats the below:

const dateTimeFormats = {
  'en-US': {
    short: {
      year: 'numeric', month: 'short', day: 'numeric'
    },
    long: {
      year: 'numeric', month: 'short', day: 'numeric',
      weekday: 'short', hour: 'numeric', minute: 'numeric'
    }
  },
  'ja-JP': {
    short: {
      year: 'numeric', month: 'short', day: 'numeric'
    },
    long: {
      year: 'numeric', month: 'short', day: 'numeric',
      weekday: 'short', hour: 'numeric', minute: 'numeric', hour12: true
    }
  }
}

As seen above, you can define named datetime format (e.g. short, long, etc), and you need to use the options with ECMA-402 Intl.DateTimeFormat

After that, when using the locale messages, you need to specify the dateTimeFormats option of the VueI18n constructor:

const i18n = new VueI18n({
  dateTimeFormats
})

new Vue({
  i18n
}).$mount('#app')

Template the below:

<div id="app">
  <p>{{ $d(new Date(), 'short') }}</p>
  <p>{{ $d(new Date(), 'long', 'ja-JP') }}</p>
</div>

Output the below:

<div id="app">
  <p>Apr 19, 2017</p>
  <p>2017年4月19日(水) 午前2:19</p>
</div>
Last Updated: 10/3/2019, 4:10:24 AM
================================================ FILE: docs/guide/directive.html ================================================ Custom directive localization | Vue I18n

# Custom directive localization

Support Version

🆕 7.3+

You can translate not only with v-t custom directive, but also with the $t method.

# String syntax

You can pass the keypath of locale messages with string syntax.

Javascript:

new Vue({
  i18n: new VueI18n({
    locale: 'en',
    messages: {
      en: { hello: 'hi there!' },
      ja: { hello: 'こんにちは!' }
    }
  }),
  data: { path: 'hello' }
}).$mount('#string-syntax')

Templates:

<div id="string-syntax">
  <!-- string literal -->
  <p v-t="'hello'"></p>
  <!-- keypath binding via data -->
  <p v-t="path"></p>
</div>

Outputs:

<div id="string-syntax">
  <p>hi there!</p>
  <p>hi there!</p>
</div>

# Object syntax

You can use object syntax.

Javascript:

new Vue({
  i18n: new VueI18n({
    locale: 'en',
    messages: {
      en: { hello: 'hi {name}!' },
      ja: { hello: 'こんにちは、{name}!' }
    }
  }),
  computed: {
    nickName () { return 'kazupon' }
  },
  data: { path: 'hello' }
}).$mount('#object-syntax')

Templates:

<div id="object-syntax">
  <!-- literal -->
  <p v-t="{ path: 'hello', locale: 'ja', args: { name: 'kazupon' } }"></p>
  <!-- data binding via data -->
  <p v-t="{ path: path, args: { name: nickName } }"></p>
</div>

Outputs:

<div id="object-syntax">
  <p>こんにちは、kazupon!</p>
  <p>hi kazupon!</p>
</div>

# Use with transitions

Support Version

🆕 8.7+

When v-t directive is applied to an element inside <transition> component, you may notice that the translated message disappears during the transition. This behavior is related to the nature of the <transition> component implementation – all directives in the disappearing element inside the <transition> component will be destroyed before the transition starts. This behavior may result in content flickering on short animations, but is most noticeable on long transitions.

To make sure directive content stays un-touched during a transition, just add the .preserve modifier to the v-t directive definition.

Javascript:

new Vue({
  i18n: new VueI18n({
    locale: 'en',
    messages: {
      en: { preserve: 'with preserve' },
    }
  }),
  data: { toggle: true }
}).$mount('#in-transitions')

Templates:

<div id="in-transitions">
  <transition name="fade">
    <span v-if="toggle" v-t.preserve="'preserve'"></span>
  </transition>
  <button @click="toggle = !toggle">Toggle</button>
</div>

It is also possible to set global settings on the VueI18n instance itself, which will affect all v-t directives without modifier.

Javascript:

new Vue({
  i18n: new VueI18n({
    locale: 'en',
    messages: {
      en: { preserve: 'with preserve' },
    },
    preserveDirectiveContent: true
  }),
  data: { toggle: true }
}).$mount('#in-transitions')

Templates:

<div id="in-transitions">
  <transition name="fade">
    <span v-if="toggle" v-t="'preserve'"></span>
  </transition>
  <button @click="toggle = !toggle">Toggle</button>
</div>

About the above examples, see the example

# $t vs v-t

# $t

$t is an extended Vue instance method. It has the following pros and cons:

# Pros

You can flexibly use mustache syntax {{}} in templates and also computed props and methods in Vue instance.

# Cons

$t is executed every time when re-render occurs, so it does have translation costs.

# v-t

v-t is a custom directive. It has the following pros and cons:

# Pros

v-t has better performance than the $t method due to its cache with the custom directive, when translated once. Also, pre-translation is possible with the Vue compiler module which was provided by vue-i18n-extensions.

Therefore it's possible to make more performance optimizations.

# Cons

v-t cannot be flexibly used like $t, it's rather complex. The translated content with v-t is inserted into the textContent of the element. Also, when you use server-side rendering, you need to set the custom directive to directives option of the createRenderer function.

Last Updated: 5/11/2020, 7:03:20 PM
================================================ FILE: docs/guide/fallback.html ================================================ Fallback localization | Vue I18n

# Fallback localization

Summary: Use fallbackLocale: '<lang>' to choose which language to use when your preferred language lacks a translation.

# Implicit fallback using locales

If a locale is given containing a territory and an optional dialect, the implicit fallback is activated automatically.

For example de-DE-bavarian would fallback

  1. de-DE-bavarian
  2. de-DE
  3. de

To suppress the automatic fallback, add the postfix exclamation mark !, for example de-DE!

# Explicit fallback with one locale

Sometimes some items will not be translated into some languages. In this example, the item hello is available in English but not Japanese:

const messages = {
  en: {
    hello: 'Hello, world!'
  },
  ja: {
  }
}

If you want to use (say) en items when an item is not available in your desired locale, set the fallbackLocale option in the VueI18n constructor:

const i18n = new VueI18n({
  locale: 'ja',
  fallbackLocale: 'en',
  messages
})

Template:

<p>{{ $t('hello') }}</p>

Output:

<p>Hello, world!</p>

By default, falling back to fallbackLocale generates two console warnings:

[vue-i18n] Value of key 'hello' is not a string!
[vue-i18n] Fall back to translate the keypath 'hello' with 'en' locale.

To suppress these warnings (while keeping those which warn of the total absence of translation for the given key) set silentFallbackWarn: true when initializing the VueI18n instance.

# Explicit fallback with an array of locales

It is possible to set more than one fallback locale by using an array of locales. For example

fallbackLocale: [ 'fr', 'en' ],

# Explicit fallback with decision maps

If more complex decision maps for fallback locales are required, it is possible to define decision maps with according fallback locales. Using the following decision map

fallbackLocale: {
  /* 1 */ 'de-CH':   ['fr', 'it'],
  /* 2 */ 'zh-Hant': ['zh-Hans'],
  /* 3 */ 'es-CL':   ['es-AR'],
  /* 4 */ 'es':      ['en-GB'],
  /* 5 */ 'pt':      ['es-AR'],
  /* 6 */ 'default': ['en', 'da']
},

will result in the following fallback chains

locale fallback chains
'de-CH' de-CH > fr > it > en > da
'de' de > en > da
'zh-Hant' zh-Hant > zh-Hans > zh > en > da
'es-SP' es-SP > es > en-GB > en > da
'es-SP!' es-SP > en > da
'fr' fr > en > da
'pt-BR' pt-BR > pt > es-AR > es > en-GB > en > da
'es-CL' es-CL > es-AR > es > en-GB > en > da

# Fallback interpolation

Summary: Set formatFallbackMessages: true to do template interpolation on translation keys when your language lacks a translation for a key.

Since the keys to the translations are strings, you can use a user-readable message (for a particular language) as a key. E.g.

const messages = {
  ja: {
    'Hello, world!': 'こんにちは、世界!'
  }
}

This is useful because you don't have to specify a translation for the string "Hello, world!" into English.

In fact, you can even include template parameters in a key. Together with formatFallbackMessages: true, this lets you skip writing templates for your "base" language; the keys are your templates.

const messages = {
  ru: {
    'Hello {name}': 'Здравствуйте {name}'
  }
}

const i18n = new VueI18n({
  locale: 'ru',
  fallbackLocale: 'en',
  formatFallbackMessages: true,
  messages
})

When the template is as below:

<p>{{ $t('Hello {name}', { name: 'John' }}) }}</p>
<p>{{ $t('The weather today is {condition}!', { condition: 'sunny' }) }}</p>

The following will be output:

<p>Здравствуйте, John</p>
<p>The weather today is sunny!</p>
Last Updated: 5/11/2020, 7:03:20 PM
================================================ FILE: docs/guide/formatting.html ================================================ Formatting | Vue I18n

# Formatting

# Named formatting

Locale messages:

const messages = {
  en: {
    message: {
      hello: '{msg} world'
    }
  }
}

Template:

<p>{{ $t('message.hello', { msg: 'hello' }) }}</p>

Output:

<p>hello world</p>

# List formatting

Locale messages:

const messages = {
  en: {
    message: {
      hello: '{0} world'
    }
  }
}

Template:

<p>{{ $t('message.hello', ['hello']) }}</p>

Output:

<p>hello world</p>

List formatting also accepts array-like objects:

<p>{{ $t('message.hello', {'0': 'hello'}) }}</p>

Output:

<p>hello world</p>

# HTML formatting

Notice

⚠️ Dynamically localizing arbitrary HTML on your website can be very dangerous because it can easily lead to XSS vulnerabilities. Only use HTML interpolation on trusted content and never on user-provided content.

We recommended using the component interpolation feature.

Notice

🆕 8.11+

You can control the use of HTML formatting. see the detail warnHtmlInMessage constructor option and property API.

In some cases you might want to render your translation as an HTML message and not a static string.

const messages = {
  en: {
    message: {
      hello: 'hello <br> world'
    }
  }
}

Template:

<p v-html="$t('message.hello')"></p>

Output (instead of the pre-formatted message )

<p>hello
<!--<br> exists but is rendered as html and not a string-->
world</p>

# Support ruby on rails i18n format

Locale messages:

const messages = {
  en: {
    message: {
      hello: '%{msg} world'
    }
  }
}

Template:

<p>{{ $t('message.hello', { msg: 'hello' }) }}</p>

Output:

<p>hello world</p>

# Custom formatting

Sometimes, you may need to translate using custom formatting (e.g. ICU message syntax).

You can do that with a custom formatter that implements the Formatter Interface.

The following custom formatter with ES2015 class syntax:

// Custom Formatter implementation
class CustomFormatter {
     constructor (options) {
       // ...
     }

     //
     // interpolate
     //
     // @param {string} message
     //   string of list or named format.
     //   e.g.
     //   - named formatting: 'Hi {name}'
     //   - list formatting: 'Hi {0}'
     //
     // @param {Object | Array} values
     //   values of `message` interpolation.
     //   passed values with `$t`, `$tc` and `i18n` functional component.
     //   e.g.
     //   - $t('hello', { name: 'kazupon' }) -> passed values: Object `{ name: 'kazupon' }`
     //   - $t('hello', ['kazupon']) -> passed values: Array `['kazupon']`
     //   - `i18n` functional component (component interpolation)
     //     <i18n path="hello">
     //       <p>kazupon</p>
     //       <p>how are you?</p>
     //     </i18n>
     //     -> passed values: Array (included VNode):
     //        `[VNode{ tag: 'p', text: 'kazupon', ...}, VNode{ tag: 'p', text: 'how are you?', ...}]`
     //
     // @return {Array<any>}
     //   interpolated values. you need to return the following:
     //   - array of string, when is using `$t` or `$tc`.
     //   - array included VNode object, when is using `i18n` functional component.
     //
     interpolate (message, values) {
       // implement interpolation logic here
       // ...

       // return the interpolated array
       return ['resolved message string']
     }
}

// register with `formatter` option
const i18n = new VueI18n({
  locale: 'en-US',
  formatter: new CustomFormatter(/* here the constructor options */),
  messages: {
    'en-US': {
      // ...
    },
    // ...
  }
})

// Run!
new Vue({ i18n }).$mount('#app')

You can check the custom formatter official example.

Last Updated: 5/11/2020, 7:03:20 PM
================================================ FILE: docs/guide/hot-reload.html ================================================ Hot reloading | Vue I18n

# Hot reloading

You can use Webpack's Hot Module Replacement (HMR) feature to watch for changes in localization files and hot reload changes into your application.

# Basic example

If you only use a static set of locales, you can hot reload those locales explicitly:

import Vue from "vue"
import VueI18n from "vue-i18n"
import en from './en'
import ja from './ja'

const messages = {
  en,
  ja
}

// VueI18n instance
const i18n = new VueI18n({
  locale: 'en',
  messages
})

// Run app
const app = new Vue({
  i18n,
  // ...
}).$mount('#app')

// Hot updates
if (module.hot) {
  module.hot.accept(['./en', './ja'], function () {
    i18n.setLocaleMessage('en', require('./en').default)
    i18n.setLocaleMessage('ja', require('./ja').default)
    // Or the following hot updates via $i18n property
    // app.$i18n.setLocaleMessage('en', require('./en').default)
    // app.$i18n.setLocaleMessage('ja', require('./ja').default)
  })
}

# Advanced example

If you want to support a changing set of locales, you can hot reload those locales dynamically using require.context:

import Vue from "vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);

// Load all locales and remember context
function loadMessages() {
  const context = require.context("./locales", true, /[a-z0-9-_]+\.json$/i);

  const messages = context
    .keys()
    .map((key) => ({ key, locale: key.match(/[a-z0-9-_]+/i)[0] }))
    .reduce(
      (messages, { key, locale }) => ({
        ...messages,
        [locale]: context(key),
      }),
      {}
    );

  return { context, messages };
}

const { context, messages } = loadMessages();

// VueI18n instance
const i18n = new VueI18n({
  locale: "en",
  messages,
});

// Run app
const app = new Vue({
  i18n,
  // ...
}).$mount('#app');

// Hot updates
if (module.hot) {
  module.hot.accept(context.id, () => {
    const { messages: newMessages } = loadMessages();

    Object.keys(newMessages)
      .filter((locale) => messages[locale] !== newMessages[locale])
      .forEach((locale) => {
        messages[locale] = newMessages[locale];
        i18n.setLocaleMessage(locale, messages[locale]);
      });
  });
}
Last Updated: 5/11/2020, 7:03:20 PM
================================================ FILE: docs/guide/interpolation.html ================================================ Component interpolation | Vue I18n

# Component interpolation

# Basic Usage

Support Version

🆕 7.0+

Sometimes, we need to localize with a locale message that was included in a HTML tag or component. For example:

<p>I accept xxx <a href="/term">Terms of Service Agreement</a></p>

In the above message, if you use $t, you will probably try to compose the following locale messages:

const messages = {
  en: {
    term1: 'I Accept xxx\'s',
    term2: 'Terms of Service Agreement'
  }
}

And your localized template may look like this:

<p>{{ $t('term1') }}<a href="/term">{{ $t('term2') }}</a></p>

Output:

<p>I accept xxx <a href="/term">Terms of Service Agreement</a></p>

This is very cumbersome, and if you configure the <a> tag in a locale message, there is a possibility of XSS vulnerabilities due to localizing with v-html="$t('term')".

You can avoid it using the i18n functional component. For example:

<div id="app">
  <!-- ... -->
  <i18n path="term" tag="label" for="tos">
    <a :href="url" target="_blank">{{ $t('tos') }}</a>
  </i18n>
  <!-- ... -->
</div>
const messages = {
  en: {
    tos: 'Term of Service',
    term: 'I accept xxx {0}.'
  },
  ja: {
    tos: '利用規約',
    term: '私は xxx の{0}に同意します。'
  }
}

const i18n = new VueI18n({
  locale: 'en',
  messages
})
new Vue({
  i18n,
  data: {
    url: '/term'
  }
}).$mount('#app')

the following output:

<div id="app">
  <!-- ... -->
  <label for="tos">
    I accept xxx <a href="/term" target="_blank">Term of Service</a>.
  </label>
  <!-- ... -->
</div>

About the above example, see the example

The children of i18n functional component are interpolated with locale message of path prop. In the above example,

<a :href="url" target="_blank">{{ $t('tos') }}</a>

is interpolated with term locale message.

In the above example, the component interpolation follows the list formatting. The children of i18n functional component are interpolated by their order of appearance.

You can choose the root container's node type by specifying a tag prop. If omitted, it defaults to 'span'. You can also set it to the boolean value false to insert the child nodes directly without creating a root element.

# Slots syntax usage

Support Version

🆕 8.14+

It's more convenient to use the named slots syntax. For example:

<div id="app">
  <!-- ... -->
  <i18n path="info" tag="p">
    <template v-slot:limit>
      <span>{{ changeLimit }}</span>
    </template>
    <template v-slot:action>
      <a :href="changeUrl">{{ $t('change') }}</a>
    </template>
  </i18n>
  <!-- ... -->
</div>
const messages = {
  en: {
    info: 'You can {action} until {limit} minutes from departure.',
    change: 'change your flight',
    refund: 'refund the ticket'
  }
}

const i18n = new VueI18n({
  locale: 'en',
  messages
})

new Vue({
  i18n,
  data: {
    changeUrl: '/change',
    refundUrl: '/refund',
    changeLimit: 15,
    refundLimit: 30
  }
}).$mount('#app')

Outputs:

<div id="app">
  <!-- ... -->
  <p>
    You can <a href="/change">change your flight</a> until <span>15</span> minutes from departure.
  </p>
  <!-- ... -->
</div>

In Vue 2.6 and later, you can use the following slots syntax in templates:

<div id="app">
  <!-- ... -->
  <i18n path="info" tag="p">
    <template #limit>
      <span>{{ changeLimit }}</span>
    </template>
    <template #action>
      <a :href="changeUrl">{{ $t('change') }}</a>
    </template>
  </i18n>
  <!-- ... -->
</div>

Limitation

⚠️ In i18n component, slots props are not supported.

# Places syntax usage

Important!!

In the next major version, the place and places props will be deprecated. Please switch to slots syntax.

Support Version

🆕 7.2+

Notice

⚠️ In i18n component, text content consisting of only white spaces will be omitted.

Named formatting is supported with the help of place attribute. For example:

<div id="app">
  <!-- ... -->
  <i18n path="info" tag="p">
    <span place="limit">{{ changeLimit }}</span>
    <a place="action" :href="changeUrl">{{ $t('change') }}</a>
  </i18n>
  <!-- ... -->
</div>
const messages = {
  en: {
    info: 'You can {action} until {limit} minutes from departure.',
    change: 'change your flight',
    refund: 'refund the ticket'
  }
}

const i18n = new VueI18n({
  locale: 'en',
  messages
})
new Vue({
  i18n,
  data: {
    changeUrl: '/change',
    refundUrl: '/refund',
    changeLimit: 15,
    refundLimit: 30
  }
}).$mount('#app')

Outputs:

<div id="app">
  <!-- ... -->
  <p>
    You can <a href="/change">change your flight</a> until <span>15</span> minutes from departure.
  </p>
  <!-- ... -->
</div>

Notice

⚠️ To use named formatting, all children of i18n component must have place attribute set. Otherwise it will fallback to list formatting.

If you still want to interpolate text content in named formatting, you could define places property on i18n component. For example:

<div id="app">
  <!-- ... -->
  <i18n path="info" tag="p" :places="{ limit: refundLimit }">
    <a place="action" :href="refundUrl">{{ $t('refund') }}</a>
  </i18n>
  <!-- ... -->
</div>

Outputs:

<div id="app">
  <!-- ... -->
  <p>
    You can <a href="/refund">refund your ticket</a> until 30 minutes from departure.
  </p>
  <!-- ... -->
</div>
Last Updated: 5/14/2020, 11:30:34 PM
================================================ FILE: docs/guide/lazy-loading.html ================================================ Lazy loading translations | Vue I18n

# Lazy loading translations

Loading all of your translation files at once is overkill and unnecessary.

Lazy loading or asynchronously loading the translation files is really easy when using Webpack.

Let´s assume we have a project directory similar to the one below:

our-cool-project
-dist
-src
--routes
--store
--setup
---i18n-setup.js
--lang
---en.js
---it.js

The lang folder is where all of our translation files reside. The setup folder is where our arbitrary setup files like the i18n-setup, global component inits, plugin inits and other reside.

//i18n-setup.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import messages from '@/lang/en'
import axios from 'axios'

Vue.use(VueI18n)

export const i18n = new VueI18n({
  locale: 'en', // set locale
  fallbackLocale: 'en',
  messages // set locale messages
})

const loadedLanguages = ['en'] // our default language that is preloaded

function setI18nLanguage (lang) {
  i18n.locale = lang
  axios.defaults.headers.common['Accept-Language'] = lang
  document.querySelector('html').setAttribute('lang', lang)
  return lang
}

export function loadLanguageAsync(lang) {
  // If the same language
  if (i18n.locale === lang) {
    return Promise.resolve(setI18nLanguage(lang))
  }

  // If the language was already loaded
  if (loadedLanguages.includes(lang)) {
    return Promise.resolve(setI18nLanguage(lang))
  }

  // If the language hasn't been loaded yet
  return import(/* webpackChunkName: "lang-[request]" */ `@/i18n/messages/${lang}.js`).then(
    messages => {
      i18n.setLocaleMessage(lang, messages.default)
      loadedLanguages.push(lang)
      return setI18nLanguage(lang)
    }
  )
}

In short we are creating a new VueI18n instance as we normally would. Then we are creating a loadedLanguages array that will keep track of our loaded languages. Next is the setI18nLanguage function that will actually change the language in our vueI18n instance, axios and where ever else is needed.

The loadLanguageAsync function is what we will actually use to change the languages. Loading the new files is done via the import function, which is generously provided by Webpack and it allows us to load files dynamically, and because it uses promises we can easily wait for the loading to finish.

You can learn more about the import function in the Webpack documentation.

Using the loadLanguageAsync function is straightforward. A common use case is inside a vue-router beforeEach hook.

router.beforeEach((to, from, next) => {
  const lang = to.params.lang
  loadLanguageAsync(lang).then(() => next())
})

We could improve this by checking if the lang is actually supported by us or not, call reject so we can catch that in the beforeEach stopping the route transition.

Last Updated: 10/3/2019, 4:32:27 AM
================================================ FILE: docs/guide/locale.html ================================================ Locale changing | Vue I18n

# Locale changing

Normally, using the root Vue instance as the starting point, all child components are localized using the locale property of the VueI18n class as a reference.

Sometimes you might want to dynamically change the locale. In that case you can change the value of the locale property of the VueI18n instance.

const i18n = new VueI18n({
  locale: 'ja', // set locale
  ...
})

// create root Vue instance
new Vue({
  i18n,
  ...
}).$mount('#app')

// change other locale
i18n.locale = 'en'

Each component contains a VueI18n instance referenced as the $i18n property that can also be used to change the locale.

Example:

<template>
  <div class="locale-changer">
    <select v-model="$i18n.locale">
      <option v-for="(lang, i) in langs" :key="`Lang${i}`" :value="lang">{{ lang }}</option>
    </select>
  </div>
</template>

<script>
export default {
  name: 'locale-changer',
  data () {
    return { langs: ['ja', 'en'] }
  }
}
</script>

Notice

⚠️ Locale changing is ignored for components that use sync: false.

Component vs. root scope

⚠️ Changing $i18n.locale inside a component does not update the root locale. If you rely on the root locale, for example when using root fallbacks, use $root.$i18n.locale instead of $i18n.locale.

Last Updated: 7/20/2019, 5:02:30 PM
================================================ FILE: docs/guide/messages.html ================================================ Locale messages syntax | Vue I18n

# Locale messages syntax

# Structure

Locale Messages syntax below:

// As Flowtype definition, Locale Messages syntax like BNF annotation
type LocaleMessages = { [key: Locale]: LocaleMessageObject };
type LocaleMessageObject = { [key: Path]: LocaleMessage };
type LocaleMessageArray = LocaleMessage[];
type LocaleMessage = string | LocaleMessageObject | LocaleMessageArray;
type Locale = string;
type Path = string;

Based on the above syntax, You can configure the following Locale Messages structure:

{
  "en": {  // 'en' Locale
    "key1": "this is message1", // basic
    "nested": { // nested
      "message1": "this is nested message1"
    },
    "errors": [ // array
      "this is 0 error code message",
      {  // object in array
        "internal1": "this is internal 1 error message"
      },
      [  // array in array
        "this is nested array error 1"
      ]
    ]
  },
  "ja": { // 'ja' Locale
    // ...
  }
}

In the above Locale Messages structure, You can translate using below key paths.

<div id="app">
  <!-- basic -->
  <p>{{ $t('key1') }}</p>
  <!-- nested -->
  <p>{{ $t('nested.message1') }}</p>
  <!-- array -->
  <p>{{ $t('errors[0]') }}</p>
  <!-- object in array -->
  <p>{{ $t('errors[1].internal1') }}</p>
  <!-- array in array -->
  <p>{{ $t('errors[2][0]') }}</p>
</div>

Output the following:

<div id="app">
  <!-- basic -->
  <p>this is message1</p>
  <!-- nested -->
  <p>this is nested message1</p>
  <!-- array -->
  <p>this is 0 error code message</p>
  <!-- object in array -->
  <p>this is internal 1 error message</p>
  <!-- array in array -->
  <p>this is nested array error 1</p>
</div>

# Linked locale messages

If there's a translation key that will always have the same concrete text as another one you can just link to it. To link to another translation key, all you have to do is to prefix its contents with an @: sign followed by the full name of the translation key including the namespace you want to link to.

Locale messages the below:

const messages = {
  en: {
    message: {
      the_world: 'the world',
      dio: 'DIO:',
      linked: '@:message.dio @:message.the_world !!!!'
    }
  }
}

Template:

<p>{{ $t('message.linked') }}</p>

Output:

<p>DIO: the world !!!!</p>

# Formatting linked locale messages

If the language distinguish cases of character, you may need control the case of the linked locale messages. Linked messages can be formatted with modifier @.modifier:key

The below modifiers are available currently.

  • upper: Uppercase all characters in the linked message.
  • lower: Lowercase all characters in the linked message.
  • capitalize: Capitalize the first character in the linked message.

Locale messages the below:

const messages = {
  en: {
    message: {
      homeAddress: 'Home address',
      missingHomeAddress: 'Please provide @.lower:message.homeAddress'
    }
  }
}
<label>{{ $t('message.homeAddress') }}</label>

<p class="error">{{ $t('message.missingHomeAddress') }}</p>

Output the below:

<label>Home address</label>

<p class="error">Please provide home address</p>

You can add modifiers or overwrite the existing ones passing the modifiers options to the VueI18n constructor.

const i18n = new VueI18n({
  locale: 'en',
  messages: {
    // ...
  },
  modifiers: {
    snakeCase: (str) => str.split(' ').join('-')
  }
})

# Grouping by brackets

A translation key of linked locale message can also have the form of @:(message.foo.bar.baz) in which the link to another translation key is within brackets ().

This can be useful if the link @:message.something is followed by period ., which otherwise would be part of the link and may not need to be.

Locale messages:

const messages = {
  en: {
    message: {
      dio: 'DIO',
      linked: 'There\'s a reason, you lost, @:(message.dio).'
    }
  }
}

Template:

<p>{{ $t('message.linked') }}</p>

Output:

<p>There's a reason, you lost, DIO.</p>
Last Updated: 5/11/2020, 7:03:20 PM
================================================ FILE: docs/guide/number.html ================================================ Number localization | Vue I18n

# Number localization

Support Version

🆕 7.0+

You can localize the number with your definition formats.

Number formats the below:

const numberFormats = {
  'en-US': {
    currency: {
      style: 'currency', currency: 'USD'
    }
  },
  'ja-JP': {
    currency: {
      style: 'currency', currency: 'JPY', currencyDisplay: 'symbol'
    }
  }
}

As the above, you can define named number formats (e.g. currency, etc), and you need to use the options with ECMA-402 Intl.NumberFormat

After that, when using the locale messages, you need to specify the numberFormats option of the VueI18n constructor:

const i18n = new VueI18n({
  numberFormats
})

new Vue({
  i18n
}).$mount('#app')

Template the below:

<div id="app">
  <p>{{ $n(100, 'currency') }}</p>
  <p>{{ $n(100, 'currency', 'ja-JP') }}</p>
</div>

Output the below:

<div id="app">
  <p>$100.00</p>
  <p>¥100</p>
</div>

# Custom formatting

Support Version

🆕 8.10+

$n method returns resulting string with fully formatted number, which can only be used as a whole. In situations when you need to style some part of the formatted number (like fraction digits), $n is not enough. In such cases <i18n-n> functional component will be of help.

With a minimum set of properties, <i18n-n> generates the same output as $n, wrapped into configured DOM element.

The following template:

<div id="app">
  <i18n-n :value="100"></i18n-n>
  <i18n-n :value="100" format="currency"></i18n-n>
  <i18n-n :value="100" format="currency" locale="ja-JP"></i18n-n>
</div>

will produce the below output:

<div id="app">
  <span>100</span>
  <span>$100.00</span>
  <span>¥100</span>
</div>

But the real power of this component comes into play when it is used with scoped slots.

Let's say there is a requirement to render the integer part of the number with a bolder font. This can be achieved by specifying integer scoped slot element:

<i18n-n :value="100" format="currency">
  <span v-slot:integer="slotProps" styles="font-weight: bold">{{ slotProps.integer }}</span>
</i18n-n>

Template above will result in the following HTML:

<span>$<span styles="font-weight: bold">100</span>.00</span>

It is possible to specify multiple scoped slots at the same time:

<i18n-n :value="1234" :format="{ key: 'currency', currency: 'EUR' }">
  <span v-slot:currency="slotProps" styles="color: green">{{ slotProps.currency }}</span>
  <span v-slot:integer="slotProps" styles="font-weight: bold">{{ slotProps.integer }}</span>
  <span v-slot:group="slotProps" styles="font-weight: bold">{{ slotProps.group }}</span>
  <span v-slot:fraction="slotProps" styles="font-size: small">{{ slotProps.fraction }}</span>
</i18n-n>

(this resulting HTML was formatted for better readability)

<span>
  <span styles="color: green"></span>
  <span styles="font-weight: bold">1</span>
  <span styles="font-weight: bold">,</span>
  <span styles="font-weight: bold">234</span>
  .
  <span styles="font-size: small">00</span>
</span>

You can choose the root container's node type by specifying a tag prop. If omitted, it defaults to 'span'. You can also set it to the boolean value false to insert the child nodes directly without creating a root element.

Full list of the supported scoped slots as well as other <i18n-n> properties can be found on API page.

Last Updated: 5/14/2020, 11:30:34 PM
================================================ FILE: docs/guide/pluralization.html ================================================ Pluralization | Vue I18n

# Pluralization

You can translate with pluralization. You must define the locale that have a pipe | separator, and define plurals in pipe separator.

Your template will need to use $tc() instead of $t().

Locale messages below:

const messages = {
  en: {
    car: 'car | cars',
    apple: 'no apples | one apple | {count} apples'
  }
}

Template below:

<p>{{ $tc('car', 1) }}</p>
<p>{{ $tc('car', 2) }}</p>

<p>{{ $tc('apple', 0) }}</p>
<p>{{ $tc('apple', 1) }}</p>
<p>{{ $tc('apple', 10, { count: 10 }) }}</p>

Output below:

<p>car</p>
<p>cars</p>

<p>no apples</p>
<p>one apple</p>
<p>10 apples</p>

# Accessing the number via the pre-defined argument

You don't need to explicitly give the number for pluralization. The number can be accessed within locale messages via pre-defined named arguments {count} and/or {n}. You can overwrite those pre-defined named arguments if necessary.

Locale messages the below:

const messages = {
  en: {
    apple: 'no apples | one apple | {count} apples',
    banana: 'no bananas | {n} banana | {n} bananas'
  }
}

Template below:

<p>{{ $tc('apple', 10, { count: 10 }) }}</p>
<p>{{ $tc('apple', 10) }}</p>

<p>{{ $tc('banana', 1, { n: 1 }) }}</p>
<p>{{ $tc('banana', 1) }}</p>
<p>{{ $tc('banana', 100, { n: 'too many' }) }}</p>

Output below:

<p>10 apples</p>
<p>10 apples</p>

<p>1 banana</p>
<p>1 banana</p>
<p>too many bananas</p>

# Custom pluralization

Such pluralization, however, does not apply to all languages (Slavic languages, for example, have different pluralization rules).

In order to implement these rules you can override the VueI18n.prototype.getChoiceIndex function.

Very simplified example using rules for Slavic languages (Russian, Ukrainian, etc.):


const defaultImpl = VueI18n.prototype.getChoiceIndex

/**
 * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`
 * @param choicesLength {number} an overall amount of available choices
 * @returns a final choice index to select plural word by
**/
VueI18n.prototype.getChoiceIndex = function (choice, choicesLength) {
  // this === VueI18n instance, so the locale property also exists here
  if (this.locale !== 'ru') {
    // proceed to the default implementation
    return defaultImpl.apply(this, arguments)
  }

  if (choice === 0) {
    return 0;
  }

  const teen = choice > 10 && choice < 20;
  const endsWithOne = choice % 10 === 1;

  if (!teen && endsWithOne) {
    return 1;
  }

  if (!teen && choice % 10 >= 2 && choice % 10 <= 4) {
    return 2;
  }

  return (choicesLength < 4) ? 2 : 3;
}

This would effectively give this:

const messages = {
  ru: {
    car: '0 машин | {n} машина | {n} машины | {n} машин',
    banana: 'нет бананов | {n} банан | {n} банана | {n} бананов'
  }
}

Where the format is 0 things | 1 thing | few things | multiple things.

Your template still needs to use $tc(), not $t():

<p>{{ $tc('car', 1) }}</p>
<p>{{ $tc('car', 2) }}</p>
<p>{{ $tc('car', 4) }}</p>
<p>{{ $tc('car', 12) }}</p>
<p>{{ $tc('car', 21) }}</p>

<p>{{ $tc('banana', 0) }}</p>
<p>{{ $tc('banana', 4) }}</p>
<p>{{ $tc('banana', 11) }}</p>
<p>{{ $tc('banana', 31) }}</p>

Which results in:

<p>1 машина</p>
<p>2 машины</p>
<p>4 машины</p>
<p>12 машин</p>
<p>21 машина</p>

<p>нет бананов</p>
<p>4 банана</p>
<p>11 бананов</p>
<p>31 банан</p>
Last Updated: 5/11/2020, 12:33:05 PM
================================================ FILE: docs/guide/sfc.html ================================================ Single file components | Vue I18n

# Single file components

# Basic Usage

If you are building Vue component or Vue application using single file components, you can manage the locale messages i18n custom block.

The following in single file components example:

<i18n>
{
  "en": {
    "hello": "hello world!"
  },
  "ja": {
    "hello": "こんにちは、世界!"
  }
}
</i18n>

<template>
  <div id="app">
    <label for="locale">locale</label>
    <select v-model="locale">
      <option>en</option>
      <option>ja</option>
    </select>
    <p>message: {{ $t('hello') }}</p>
  </div>
</template>

<script>
export default {
  name: 'app',
  data () {
    this.$i18n.locale = 'en';
    return { locale: 'en' }
  },
  watch: {
    locale (val) {
      this.$i18n.locale = val
    }
  }
}
</script>

# Installing vue-i18n-loader

You need to install vue-loader and vue-i18n-loader due to use <i18n> custom blocks. While vue-loader most likely is already used in your project if you are working with single file components, you must install vue-i18n-loader additionally:

npm i --save-dev @intlify/vue-i18n-loader@next

# Webpack

For Webpack the configuration below is required:

for vue-loader v15 or later:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
      },
      {
        resourceQuery: /blockType=i18n/,
        type: 'javascript/auto',
        loader: '@intlify/vue-i18n-loader'
      }
      // ...
    ]
  },
  // ...
}

for vue-loader v14:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            // you need to specify `i18n` loaders key with `vue-i18n-loader` (https://github.com/kazupon/vue-i18n-loader)
            i18n: '@intlify/vue-i18n-loader'
          }
        }
      },
      // ...
    ]
  },
  // ...
}

# Vue CLI 3.0

Vue CLI 3.0 hides the webpack configuration, so, if we want to add support to the <i18n> tag inside a single file component we need to modify the existing configuration.

In order to do that we have to create a vue.config.js at the root of our project. Once we have done that, we have to include the following:

for vue-loader v15 or later:

module.exports = {
  chainWebpack: config => {
    config.module
      .rule("i18n")
      .resourceQuery(/blockType=i18n/)
      .type('javascript/auto')
      .use("i18n")
        .loader("@intlify/vue-i18n-loader")
        .end();
  }
}

for vue-loader v14:

const merge = require('deepmerge')

module.exports = {
  chainWebpack: config => {
    config.module
      .rule('vue')
      .use('vue-loader')
      .tap(options =>
        merge(options, {
          loaders: {
            i18n: '@intlify/vue-i18n-loader'
          }
        })
      )
  }
}

Don 't forget to install deepmerge! (npm i deepmerge -D or yarn add deepmerge -D)

If you want to read more about modifying the existing configuration click here.

# Laravel-Mix

for Laravel-mix 4 with vue-loader v15 or later:

// Extend Mix with the "i18n" method, that loads the vue-i18n-loader
mix.extend( 'i18n', new class {
        webpackRules() {
            return [
                {
                    resourceQuery: /blockType=i18n/,
                    type:          'javascript/auto',
                    loader:        '@intlify/vue-i18n-loader',
                },
            ];
        }
    }(),
);

// Make sure to call the .i18n() (to load the loader) before .js(..., ...)
mix.i18n()
   .js( 'resources/js/App.js', 'public/js/app.js' )
   ...

for Laravel-mix 2 with vue-loader v14:

As of V2.1 of Laravel-mix, you can add custom rules via mix.extend(). Laravel mix already has its own rules for handling .vue files. To add the vue-i18n-loader, add the following to webpack.mix.js

// The below code will inject i18n Kazupon/vue-18-loader as a loader for .vue files.
mix.extend( 'i18n', function( webpackConfig, ...args ) {
    webpackConfig.module.rules.forEach( ( module ) => {
        // Search for the "vue-loader" component, which handles .vue files.
        if( module.loader !== 'vue-loader' ) {
            return;
        }

        // Within this module, add the vue-i18n-loader for the i18n tag.
        module.options.loaders.i18n = '@intlify/vue-i18n-loader';
    } );
} );

// Make sure to call .i18n() before .js(..., ...)
mix.i18n()
   .js( 'resources/assets/js/App.js', 'public/js/app.js' )
   ...

# YAML loading

i18n custom blocks need to specify JSON format, also you can use YAML format by using pre-loader feature of vue-loader.

the i18n custom blocks below of YAML format:

<i18n>
en:
  hello: "hello world!"
ja:
  hello: "こんにちは、世界!"
</i18n>

Webpack conf the below:

for vue-loader v15 or later:

// Vue CLI 3.0
module.exports = {
  chainWebpack: config => {
    config.module
      .rule("i18n")
      .resourceQuery(/blockType=i18n/)
      .type('javascript/auto')
      .use("i18n")
        .loader("@intlify/vue-i18n-loader")
        .end()
      .use('yaml')
        .loader('yaml-loader')
        .end()
  }
}

for vue-loader v14:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          preLoaders: {
            i18n: 'yaml-loader'
          },
          loaders: {
            i18n: '@intlify/vue-i18n-loader'
          }
        }
      },
      // ...
    ]
  },
  // ...
}

# Multiple custom blocks

You can use locale messages with multiple i18n custom blocks.

<i18n src="./common/locales.json"></i18n>
<i18n>
  {
    "en": {
      "hello": "hello world!"
    },
    "ja": {
      "hello": "こんにちは、世界!"
    }
  }
</i18n>

In the above, first custom block load the common locale message with src attribute, second custom block load the locale message that is defined only at single file component. These locale messages will be merged as locale message of component.

In this way, multiple custom blocks useful when want to be used as module.

# Scoped style

When using vue-i18n with scoped style, it's important to remember to use a deep selector for styling an element inside the translation string. For example:

Translation contains only text (Work without deep selector)

...
<i18n>
{
  "en": {
    "hello": "hello world!"
  },
  "ja": {
    "hello": "こんにちは、世界"
  }
}
</i18n>
...
<template>
  <div class="parent">
    <p>message: {{ $t('hello') }}</p>
  </div>
</template>
...
<!-- Will work -->
<style>
.parent p {
  color: #42b883;
}
</style>

Translation with HTML element (Must use deep selector)

...
<i18n>
{
  "en": {
    "hello": "hello<span>world!</span>"
  },
  "ja": {
    "hello": "こんにちは、<span>世界!</span>"
  }
}
</i18n>
...
<template>
  <div class="parent">
    <p v-html="$t('hello')"></p>
  </div>
</template>
...
<!-- Won't work -->
<style>
.parent p {
  color: #42b883;
}

.parent p span{
  color: red;
}
</style>

<!-- Will work -->
<style>
.parent p {
  color: #42b883;
}

.parent p >>> span{
  color: red;
}
</style>

# Custom blocks in functional component

If the single file components have the template using a functional component, and you had been defined i18n custom blocks, note you cannot localize using locale messages.

For example, the following code cannot localize with the locale message of i18n custom block.

<i18n>
{
  "en": {
    "hello": "hello world"
  },
  "ja": {
    "hello": "こんにちは、世界"
  }
}
</i18n>

<template functional>
  <!-- 'hello' of locale messages of parent instance -->
  <p>{{ parent.$t('hello') }}</p>
</template>
Last Updated: 5/11/2020, 7:03:20 PM
================================================ FILE: docs/guide/tooling.html ================================================ Tooling | Vue I18n

# Tooling

To support the i18n of Vue applications, some tools are officially provided.

There are also tools from third vendors integrating Vue I18n.

# Official tooling

# Vue CLI Plugin

vue-cli-plugin-i18n is officially provided as the Vue CLI Plugin.

With this plugin, you can setup the i18n environment for the Vue application, and support the i18n development environment.

# Nuxt Module

nuxt-i18n is corresponding Nuxt.js module.

# Webpack loader

vue-i18n-loader is an officially provided webpack loader.

With this loader, you can use the i18n custom block in the Single file components.

In about i18n custom block, see the Single file components section

# ESLint Plugin

eslint-plugin-vue-i18n is ESLint plugin for Vue I18n.

It easily integrates some localization lint features to your Vue.js Application.

# Extensions

vue-i18n-extensions provides some extensions for Vue I18n.

You can use this extension to enable SSR and improve i18n performance.

# 3rd party tooling

# BabelEdit

BabelEdit is translation editor for web apps.

BabelEdit can translate json files, and it can also translate i18n custom block of Single-file components.

Read more about BabelEdit in tutorial page.

# i18n Ally

i18n Ally is i18n extension for VSCode.

i18n Ally give awesome DX for your i18n development.

Read more about i18n Ally in README.

# i18nPlugin (intellij platform)

i18nPlugin Intellij idea i18next support plugin ( Jetbrains plugin page ).

Plugin for i18n typescript/javascript/PHP. Supports vue-i18n. To enable vue-i18n support go to settings -> Tools -> i18n Plugin configuration and check "Vue-i18n". You need set vue locales directory (locales by default).

Last Updated: 5/13/2020, 3:58:49 PM
================================================ FILE: docs/index.html ================================================ Vue I18n ================================================ FILE: docs/installation.html ================================================ Installation | Vue I18n

# Installation

# Compatibility Note

  • Vue.js 2.0.0+

# Direct Download / CDN

https://unpkg.com/vue-i18n/dist/vue-i18n

unpkg.com provides NPM-based CDN links. The above link will always point to the latest release on NPM. You can also use a specific version/tag via URLs like https://unpkg.com/vue-i18n@8.17.5/dist/vue-i18n.js

Include vue-i18n after Vue and it will install itself automatically:

<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-i18n/dist/vue-i18n.js"></script>

# NPM

npm install vue-i18n

# Yarn

yarn add vue-i18n

When using with a module system, you must explicitly install the vue-i18n via Vue.use():

import Vue from 'vue'
import VueI18n from 'vue-i18n'

Vue.use(VueI18n)

You don't need to do this when using global script tags.

# Vue Cli 3.x

vue add i18n

You need Vue cli 3.x as pre-requisite, you can install it on your shell with the next command:

npm install @vue/cli -g

# Dev Build

You will have to clone directly from GitHub and build vue-i18n yourself if you want to use the latest dev build.

git clone https://github.com/kazupon/vue-i18n.git node_modules/vue-i18n
cd node_modules/vue-i18n
npm install # or `yarn`
npm run build  # or `yarn run build`

# Explanation of Different Builds

In the dist/ directory of the NPM package you will find many different builds of VueI18n. Here’s an overview of the difference between them:

  • UMD: vue-i18n.js
  • CommonJS: vue-i18n.common.js
  • ES Module for bundlers: vue-i18n.esm.js
  • ES Module for browsers: vue-i18n.esm.browser.js

# Terms

  • UMD: UMD builds can be used directly in the browser via a <script> tag. The default file from Unpkg CDN at https://unpkg.com/vue-i18n is the UMD build (vue-i18n.js).

  • CommonJS: CommonJS builds are intended for use with older bundlers like browserify or webpack 1. The default file for these bundlers (pkg.main) is the Runtime only CommonJS build (vue-i18n.common.js).

  • ES Module: starting in 8.11 VueI18n provides two ES Modules (ESM) builds:

    • ESM for bundlers: intended for use with modern bundlers like webpack 2 or Rollup. ESM format is designed to be statically analyzable so the bundlers can take advantage of that to perform "tree-shaking" and eliminate unused code from your final bundle. The default file for these bundlers (pkg.module) is the Runtime only ES Module build (vue-i18n.esm.js).
    • ESM for browsers (8.11+ only, vue-i18n.esm.browser.js): intended for direct imports in modern browsers via <script type="module">.
Last Updated: 5/14/2020, 11:30:34 PM
================================================ FILE: docs/introduction.html ================================================ Introduction | Vue I18n

# Introduction

Notice

⚠️ This documentation is for Vue I18n v6.0 or later. if you use v5.x, see the legacy section.

Vue I18n is internationalization plugin of Vue.js. It easily integrates some localization features to your Vue.js Application.

Go to Get Started

# Sponsors

# Silver

# Bronze

# Become a Patreon

Is your company using vue-i18n or vue-cli-plugin-i18n to build awesome apps? Join the other patrons and become a sponsor to add your logo on this documentation! Supporting me on Patreon allows me to work less for a job and to work more on Free Open Source Software such as vue-i18n! Thank you!

Become a Patreon

Last Updated: 4/22/2020, 9:02:29 AM
================================================ FILE: docs/legacy/index.html ================================================ Migration from v5.x | Vue I18n

# Migration from v5.x

# Global config

# lang replaced

Use VueI18n class constructor locale option, or VueI18n#locale property:

  const i18n = new VueI18n({
    locale: 'en',
    // ...
  })
  const app = new Vue({ i18n }).$mount('#app')

  // change locale
  i18n.locale = 'ja'
  // or
  app.$i18n.locale = 'ja'

# fallbackLang replaced

Use VueI18n class constructor fallbackLocale option, or VueI18n#fallbackLocale property:

  const i18n = new VueI18n({
    locale: 'ja',
    fallbackLocale: 'en',
    // ...
  })
  const app = new Vue({ i18n }).$mount('#app')

  // change fallback locale
  i18n.fallbackLocale = 'zh'
  // or
  app.$i18n.fallbackLocale = 'zh'

# missingHandler replaced

Use VueI18n class constructor missing option, or VueI18n#missing property:

const i18n = new VueI18n({
  // ...
  missing: (locale, key, vm, values) => {
    // handle translation missing
  },
  // ...
})
const app = new Vue({ i18n }).$mount('#app')

// change missing handler
i18n.missing = (locale, key, vm, values) => {
  // handle translation missing
}
// or
app.$i18n.missing = (locale, key, vm, values) => {
  // handle translation missing
}

# i18nFormatter replaced

Use VueI18n class constructor formatter option, or VueI18n#formatter property:

  class CustomFormatter {
    format (message, ...values) {
      // something render logic
      return 'something string'
    }
  }

  const i18n = new VueI18n({
    // ...
    formatter: new CustomFormatter(),
    // ...
  })
  const app = new Vue({ i18n }).$mount('#app')

  // change custom formatter
  i18n.formatter = {
    format: (message, ...values) => {
      // something render logic
      return 'something string'
    }
  }
  // or
  app.$i18n.formatter = {
    format: (message, ...values) => {
      // something render logic
      return 'something string'
    }
  }

# Global methods

# Vue.locale replaced

Use VueI18n class constructor messages option, or VueI18n#GetLocaleMessage / VueI18n#setLocaleMessage methods:

  const i18n = new VueI18n({
    // ...
    messages: {
      en: {
        hello: 'hello world',
        // ...
      },
      ja: {
        hello: 'こんにちは、世界',
        // ...
      }
    },
    // ...
  })
  const app = new Vue({ i18n }).$mount('#app')

  // get locale message
  const en = i18n.getLocaleMessage('en')
  en.greeting = 'hi!'
  // set locale message
  i18n.setLocaleMessage('en', en)
  // or
  const ja = app.$i18n.getLocaleMessage('ja')
  ja.greeting = 'やあ!'
  app.$i18n.setLocaleMessage('ja', ja)

# Vue.t replaced

Use VueI18n#t method:

const i18n = new VueI18n({
  locale: 'en',
  messages: {
    en: {
      greeting: 'hi {name}'
    }
  },
  // ...
})

i18n.t('greeting', { name: 'kazupon' }) // -> hi kazupon

# Vue.tc replaced

Use VueI18n#tc method:

const i18n = new VueI18n({
  locale: 'en',
  messages: {
    en: {
      apple: 'no apples | one apple | {count} apples'
    }
  },
  // ...
})

const count = 10
i18n.tc('apple', count, { count }) // -> 10 apples

# Vue.te replaced

Use VueI18n#te method:

const i18n = new VueI18n({
  locale: 'en',
  messages: {
    en: {
      hello: 'hello world'
    }
  },
  // ...
})

i18n.te('hello') // -> true
i18n.te('hallo', 'ja') // -> false
i18n.te('hello') // -> true

# Constructor options

# locales replaced

Use messages of VueI18n class constructor option, or messages of i18n option (for Component option):

const i18n = new VueI18n({
  locale: 'en',
  messages: {
    en: {
      greeting: 'hi {name}'
    }
  },
  // ...
})

// for Component
const Component1 = {
  i18n: {
    messages: {
      en: {
        title: 'Title1'
      }
    }
  }
}

# Instance properties

# $lang replaced

Use VueI18n#locale property:

const i18n = new VueI18n({
  locale: 'en',
  // ...
})
const app = new Vue({ i18n }).$mount('#app')

// change locale
i18n.locale = 'ja'
// or
app.$i18n.locale = 'ja'

# Features

# Dynamic locale removed

If you need to dynamic set locale messages, you should implement the below:

const i18n = new VueI18n({ locale: 'en' })
const app = new Vue({
  i18n,
  data: { loading: '' }
}).$mount('#app')

function loadLocaleMessage (locale, cb) {
  return fetch('/locales/' + locale, {
    method: 'get',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    }
  }).then((res) => {
    return res.json()
  }).then((json) => {
    if (Object.keys(json).length === 0) {
      return Promise.reject(new Error('locale empty !!'))
    } else {
      return Promise.resolve(json)
    }
  }).then((message) => {
    cb(null, message)
  }).catch((error) => {
    cb(error)
  })
}

app.loading = 'loading ...'
loadLocaleMessage('en', (err, message) => {
  if (err) {
    app.loading = ''
    console.error(err)
    return
  }
  i18n.setLocaleMessage('en', message)
  app.loading = ''
})
Last Updated: 9/22/2019, 11:26:01 PM
================================================ FILE: docs/legacy/v5.html ================================================ documentation for v5.x | Vue I18n ================================================ FILE: docs/ru/api/index.html ================================================ Справочник API | Vue I18n

# Справочник API

# Расширение прототипа Vue

# Опции конструктора Vue

# i18n

  • Тип: I18nOptions

Опция локализации на основе компонентов.

  • См. также: Опции конструктора класса VueI18n

# Внедряемые методы

# $t

  • Аргументы:

    • {Path} key: обязательный
    • {Locale} locale: опционально
    • {Array | Object} values: опционально
  • Возвращает: TranslateResult

Получение переведённого сообщения по ключу key. Сообщения локализации в компоненте имеют приоритет над глобальными сообщениями. Если сообщений локализации в компоненте нет, то локализация осуществляется с помощью глобальных сообщений локализации. Если указана locale, то используются сообщения локализации из locale. Если был указан key именованного формата / формата списков сообщений локализации, то необходимо указывать также values. Подробнее про значения values можно изучить в разделе Формат сообщений локализации.

Совет

Обратите внимание, что в хуках жизненного цикла контекст должен быть экземпляром компонента (например в опции data, const $t = this.$t.bind(this)).

# $tc

  • Аргументы:

    • {Path} key: обязательный
    • {number} choice: опционально, по умолчанию 1
    • {Locale} locale: опционально
    • {string | Array | Object} values: опционально
  • Возвращает: TranslateResult

Получение переведённого сообщения по ключу key с использованием плюрализации. Сообщения локализации компонента имеют приоритет над глобальными сообщениями. Если сообщений локализации в компоненте нет, то локализация осуществляется с помощью глобальных сообщений локализации. Если указана locale, то используются сообщения локализации из locale. Если указано строковое значение для values, то локализация выполняется для этого значения. Если указано значение Array или Object в values, то необходимо указывать с values из $t.

Совет

Обратите внимание, что в хуках жизненного цикла контекст должен быть экземпляром компонента (например в опции data, const $tc = this.$tc.bind(this)).

# $te

  • Аргументы:

    • {Path} key: обязательный
    • {Locale} locale: опционально
  • Возвращает: boolean

Проверяет существует ли перевод для ключа в сообщениях локализации. Если нет сообщений локализации в компоненте, то проверяет в глобальных сообщениях локализации. Если указана locale, то проверяется наличие в сообщениях локализации locale.

Совет

Обратите внимание, что в хуках жизненного цикла контекст должен быть экземпляром компонента (например в опции data, const $te = this.$te.bind(this)).

# $d

🆕 Добавлено в версии 7.0+

  • Аргументы:

    • {number | Date} value: обязательный
    • {Path | Object} key: опционально
    • {Locale | Object} locale: опционально
  • Возвращает: DateTimeFormatResult

Локализация даты из value по указанному формату даты из key. Формат указанный в key должен быть зарегистрирован в опции dateTimeFormats класса VueI18n, и зависит от опции locale конструктора VueI18n. Если указать аргумент locale, то он будет иметь приоритет над опцией locale конструктора VueI18n.

Если формата даты для key нет в опции dateTimeFormats, то будет использован запасной формат, основываясь на опции fallbackLocale конструктора VueI18n.

Совет

Обратите внимание, что в хуках жизненного цикла контекст должен быть экземпляром компонента (например в опции data, const $d = this.$d.bind(this)).

# $n

🆕 Добавлено в версии 7.0+

  • Аргументы:

    • {number} value: обязательный
    • {Path | Object} format: опционально
    • {Locale} locale: опционально
  • Возвращает: NumberFormatResult

Локализация числа value с помощью формата чисел format. Числовой формат из format должен быть зарегистрирован в опции numberFormats класса VueI18n, и зависит от опции locale конструктора VueI18n. Если указать аргумент locale, то он будет иметь приоритет над опцией locale конструктора VueI18n.

Если формат чисел для format не указан в опции numberFormats, будет использован запасной формат, основываясь на опции fallbackLocale конструктора VueI18n.

Если второй аргумент format указан объектом, то в нём должны быть следующие свойства:

  • key {Path}: опционально, форматируемое число
  • locale {Locale}: опционально, локализация
  • style {string}: опционально, опция форматирования чисел
  • currency {string}: опционально, опция форматирования чисел
  • currencyDisplay {string}: опционально, опция форматирования чисел
  • useGrouping {string}: опционально, опция форматирования чисел
  • minimumIntegerDigits {string}: опционально, опция форматирования чисел
  • minimumFractionDigits {string}: опционально, опция форматирования чисел
  • maximumFractionDigits {string}: опционально, опция форматирования чисел
  • minimumSignificantDigits {string}: опционально, опция форматирования чисел
  • maximumSignificantDigits {string}: опционально, опция форматирования чисел
  • localeMatcher {string}: опционально, опция форматирования чисел
  • formatMatcher {string}: опционально, опция форматирования чисел

Любые указанные опции форматирования числа будут иметь приоритет над значениями numberFormats из конструктора VueI18n.

Совет

Обратите внимание, что в хуках жизненного цикла контекст должен быть экземпляром компонента (например в опции data, const $n = this.$n.bind(this)).

# Внедряемые свойства

# $i18n

  • Тип: I18n

  • Только для чтения

Получение экземпляра VueI18n, если был определён.

Если в компоненте указана опция i18n, то получение экземпляра VueI18n компонента. В противном случае, получение корневого экземпляра VueI18n.

# Класс VueI18n

Класс VueI18n реализует интерфейс I18n из определений flowtype

# Статические свойства

# version

  • Тип: string

Версия vue-i18n.

# availabilities

🆕 Добавлено в версии 7.0+

  • Тип: IntlAvailability

Проверка доступности следующих возможностей интернационализации:

  • {boolean} dateTimeFormat: форматирование дат для локалей

  • {boolean} numberFormat: форматирование чисел для локалей

Указанные выше возможности интернационализации зависят от окружения браузера, в котором реализован ECMAScript Internationalization API (ECMA-402).

# Опции конструктора

Можно указывать некоторые опции конструктора I18nOptions, основываясь на определениях flowtype

# locale

  • Тип: Locale

  • По умолчанию: 'en-US'

Локаль используемая для локализации. Если локаль содержит территорию и диалект, то эта локаль явно определяет запасную локализацию.

# fallbackLocale

  • Тип: FallbackLocale

  • По умолчанию: false

Запасная локаль для локализации. Подробнее в разделе Запасная локализация.

# messages

  • Тип: LocaleMessages

  • По умолчанию: {}

Сообщения локализации для локали.

# dateTimeFormats

🆕 Добавлено в версии 7.0+

  • Тип: DateTimeFormats

  • По умолчанию: {}

Форматы дат для локализации.

# numberFormats

🆕 Добавлено в версии 7.0+

  • Тип: NumberFormats

  • По умолчанию: {}

Форматы чисел для локализации.

# availableLocales

🆕 Добавлено в версии 8.9.0+

  • Тип: Locale[]

  • По умолчанию: []

  • Примеры: ["en", "ru"]

Список доступных локалей в messages в лексическом порядке.

# formatter

  • Тип: Formatter

  • По умолчанию: Встроенный formatter

Метод форматирования, реализующий интерфейс Formatter.

# modifiers

🆕 Добавлено в версии 8.15.0+

  • Тип: Modifier

  • По умолчанию: модификаторы lower и upper

Функции модификаторов для связанных сообщений

# missing

  • Тип: MissingHandler

  • По умолчанию: null

Обработчик для отсутствующих сообщений локализации. Обработчик будет вызван с локалью, ключом сообщения локализации и значениями values.

Если указан этот обработчик и произойдёт попытка доступа к отсутствующему сообщению локализации, то предупреждения в консоли не будет.

# fallbackRoot

  • Тип: Boolean

  • По умолчанию: true

При использовании локализации в компонентах определяет обращаться ли к локализации корневого уровня (глобальной) при неудаче.

При значении false будет выбрасываться предупреждение и возвращаться ключ.

# sync

  • Тип: Boolean

  • По умолчанию: true

Синхронизировать ли локализацию корневого уровня с локализацией компонента.

При значении false, независимо от локали определённой на корневом уровне, будет использоваться локаль установленная в компоненте.

# silentTranslationWarn

6.1+, 🆙 8.13

  • Тип: Boolean | RegExp

  • По умолчанию: false

Отключение предупреждений, отображаемых при неудаче локализации.

При значении true, отключаются предупреждения об ошибках локализации. Если использовать регулярное выражение, то можно отключать предупреждения об ошибках, которые будут соответствовать key (например, $t).

# silentFallbackWarn

🆕 Добавлено в версии 8.8+, 🆙 8.13

  • Тип: Boolean | RegExp

  • По умолчанию: false

Отключение предупреждений при использовании запасной локали из fallbackLocale или root.

При значении true предупреждения будут генерироваться только если недоступна локализация вообще, а не для случаев использования запасной локали. При использовании регулярного выражения можно отключать предупреждения которые будут соответствовать key (например, $t).

# preserveDirectiveContent

Добавлено в версии 8.7+

  • Тип: Boolean

  • По умолчанию: false

Определяет должен ли элемент директивы v-t сохранять textContent после того как директива будет снята с элемента.

# warnHtmlInMessage

Добавлено в версии 8.11+

  • Тип: WarnHtmlInMessageLevel

  • По умолчанию: off

Разрешить ли использование HTML-форматирования в сообщениях локализации. См. также свойство warnHtmlInMessage.

Внимание!

Со следующей мажорной версии значение по умолчанию warnHtmlInMessage будет warn.

# sharedMessages

Добавлено в версии 8.12+

  • Тип: LocaleMessages

  • По умолчанию: undefined

Общие сообщения локализации при локализации в компонентах. Подробнее в разделе Локализация на основе компонентов.

# postTranslation

Добавлено в версии 8.16+

  • Тип: PostTranslationHandler

  • По умолчанию: null

Пост-обработчик локализации. Выполняется после вызова $t, t, $tc и tc.

Может пригодиться при необходимости дополнительно обработать итоговый текст перевода, например избавиться от висящих пробелов.

# Свойства

# locale

  • Тип: Locale

  • Чтение/Запись

Локаль используемая для локализации. Если локаль содержит территорию и диалект, то эта локаль неявно указывает на запасные варианты.

# fallbackLocale

  • Тип: FallbackLocale

  • Чтение/Запись

Локаль используемая для запасной локализации. Способы определения и переключения на запасную локализацию можно изучить в разделе Запасная локализация.

# messages

  • Тип: LocaleMessages

  • Только для чтения

Переведённые сообщения используемые для локализации.

# dateTimeFormats

🆕 Добавлено в версии 7.0+

  • Тип: DateTimeFormats

  • Только для чтения

Форматы форматирования дат для локализации.

# numberFormats

🆕 Добавлено в версии 7.0+

  • Тип: NumberFormats

  • Только для чтения

Форматы форматирования чисел для локализации.

# missing

  • Тип: MissingHandler

  • Чтение/Запись

Обработчик для отсутствующих ключей локализаций.

# formatter

  • Тип: Formatter

  • Чтение/Запись

Метод форматирования, который реализует интерфейс Formatter.

# silentTranslationWarn

6.1+, 🆙 8.13

  • Тип: Boolean | RegExp

  • Чтение/Запись

Отключение предупреждений выводимых при ошибке локализации.

# silentFallbackWarn

🆕 Добавлено в версии 8.8+, 🆙 8.13

  • Тип: Boolean | RegExp

  • Чтение/Запись

Отключение предупреждений выводимых при ошибке использования запасной локализации.

# preserveDirectiveContent

Добавлено в версии 8.7+

  • Тип: Boolean

  • Чтение/Запись

Должен ли элемент директивы v-t сохранять textContent после того как директива снята с элемента.

# warnHtmlInMessage

Добавлено в версии 8.11+

  • Тип: WarnHtmlInMessageLevel

  • Чтение/Запись

Разрешить ли использование HTML-форматирования в сообщениях локализации.

  • При установке warn или error проверяются сообщения локализации экземпляра VueI18n.
  • При установке warn выводятся предупреждения в консоль.
  • При установке error генерируется Error.

В качестве значения по умолчанию в экземпляре VueI18n установлено off.

# postTranslation

Добавлено в версии 8.16+

  • Тип: PostTranslationHandler

  • Чтение/Запись

Обработчик для пост-обработки перевода.

# Методы

# getChoiceIndex

  • Аргументы:

    • {number} choice
    • {number} choicesLength
  • Возвращает: finalChoice {number}

Получение индекса для плюрализации текущего числа и заданного количества вариантов. Реализация может быть переопределена через изменение прототипа:

VueI18n.prototype.getChoiceIndex = /* пользовательская реализация */

# getLocaleMessage( locale )

  • Аргументы:

    • {Locale} locale
  • Возвращает: LocaleMessageObject

Получение сообщений локализации для локали.

# setLocaleMessage( locale, message )

  • Аргументы:

    • {Locale} locale
    • {LocaleMessageObject} message

Установка сообщений локализации для локали.

ПРИМЕЧАНИЕ

Добавлено в версии 8.11+

При использовании warn или error в свойстве warnHtmlInMessage, при выполнении этого метода будет проверено используется ли HTML-форматирование для сообщения локализации.

# mergeLocaleMessage( locale, message )

6.1+

  • Аргументы:

    • {Locale} locale
    • {LocaleMessageObject} message

Объединение указанных сообщений локализации с сообщениями локализации локали.

ПРИМЕЧАНИЕ

Добавлено в версии 8.11+

При использовании warn или error в свойстве warnHtmlInMessage, при выполнении этого метода будет проверено используется ли HTML-форматирование для сообщения локализации.

# t( key, [locale], [values] )

  • Аргументы:

    • {Path} key: обязательный
    • {Locale} locale: опционально
    • {Array | Object} values: опционально
  • Возвращает: : TranslateResult

Аналогично функции возвращаемой методом $t. Подробнее см. $t.

# tc( key, [choice], [values] )

  • Аргументы:

    • {Path} key: обязательный
    • {number} choice: опционально, по умолчанию 1
    • {string | Array | Object} values: опционально
  • Возвращает: TranslateResult

Аналогично функции возвращаемой методом $tc. Подробнее см. $tc.

# te( key, [locale] )

  • Аргументы:

    • {string} key: обязательный
    • {Locale} locale: опционально
  • Возвращает: boolean

Проверяет существует ли указанный ключ в глобальных сообщениях локализации. Если указать locale, проверка будет осуществляться в сообщениях локализации locale.

# getDateTimeFormat ( locale )

🆕 Добавлено в версии 7.0+

  • Аргументы:

    • {Locale} locale
  • Возвращает: DateTimeFormat

Получение форматов форматирования дат локализации.

# setDateTimeFormat ( locale, format )

🆕 Добавлено в версии 7.0+

  • Аргументы:

    • {Locale} locale
    • {DateTimeFormat} format

Установка форматов форматирования дат для локализации.

# mergeDateTimeFormat ( locale, format )

🆕 Добавлено в версии 7.0+

  • Аргументы:

    • {Locale} locale
    • {DateTimeFormat} format

Объединение указанных форматов форматирования дат с форматами локализации.

# d( value, [key], [locale] )

🆕 Добавлено в версии 7.0+

  • Аргументы:

    • {number | Date} value: обязательный
    • {Path | Object} key: опционально
    • {Locale | Object} locale: опционально
  • Возвращает: DateTimeFormatResult

Аналогично функции возвращаемой методом $d. Подробнее см. $d.

# getNumberFormat ( locale )

🆕 Добавлено в версии 7.0+

  • Аргументы:

    • {Locale} locale
  • Возвращает: NumberFormat

Получение форматов форматирования чисел для локализации.

# setNumberFormat ( locale, format )

🆕 Добавлено в версии 7.0+

  • Аргументы:

    • {Locale} locale
    • {NumberFormat} format

Установка форматов форматирования чисел для локализации.

# mergeNumberFormat ( locale, format )

🆕 Добавлено в версии 7.0+

  • Аргументы:

    • {Locale} locale
    • {NumberFormat} format

Объединение указанных форматов форматирования чисел с форматами локализации.

# n( value, [format], [locale] )

🆕 Добавлено в версии 7.0+

  • Аргументы:

    • {number} value: обязательный
    • {Path | Object} format: опционально
    • {Locale} locale: опционально
  • Возвращает: NumberFormatResult

Аналогично функции возвращаемой методом $n. Подробнее см. $n.

# Директивы

🆕 Добавлено в версии 7.3+

# v-t

  • Ожидает: string | Object

  • Модификаторы:

    • .preserve: (8.7.0+) сохраняет textContent элемента при снятии директивы с элемента.
  • Подробности:

Обновление textContent элемента, который был переведён с помощью сообщений локализации. Можно использовать строковый или объектный синтаксис. Строковый синтаксис может быть задан в качестве пути к сообщению локализации. При использовании объектного синтаксиса необходимо указать следующие свойства:

* path: обязательный, ключ сообщения локализации
* locale: опционально, локализация
* args: опционально, для списка или именованного форматирования

ПРИМЕЧАНИЕ

По умолчанию значение textContent элемента удаляется при снятии директивы v-t. Это может быть нежелательной ситуацией например при анимировании списков. Для сохранения данных textContent после снятия директивы следует использовать модификатор .preserve или глобальную опцию preserveDirectiveContent.

  • Примеры:
<!-- строковый синтаксис: литерал -->
<p v-t="'foo.bar'"></p>

<!-- строковый синтаксис: привязка к данным -->
<p v-t="msg"></p>

<!-- объектный синтаксис: литерал -->
<p v-t="{ path: 'hi', локализация: 'ru', args: { name: 'kazupon' } }"></p>

<!-- объектный синтаксис: привязка к данным -->
<p v-t="{ path: greeting, args: { name: fullName } }"></p>

<!-- с модификатором preserve -->
<p v-t.preserve="'foo.bar'"></p>

# Компоненты

# Функциональный компонент i18n

🆕 Добавлено в версии 7.0+

# Входные параметры:

  • path {Path}: обязательный, путь к сообщению локализации
  • locale {Locale}: опционально, локализация
  • tag {string}: опционально, по умолчанию span
  • places {Array | Object}: опционально (7.2+)

Внимание!

Со следующей мажорной версии опция places будет удалена. Используйте синтаксис слотов.

# Использование:

<div id="app">
  <!-- ... -->
  <i18n path="term" tag="label" for="tos">
    <a :href="url" target="_blank">{{ $t('tos') }}</a>
  </i18n>
  <!-- ... -->
</div>
const messages = {
  en: {
    tos: 'Term of Service',
    term: 'I accept xxx {0}.'
  },
  ru: {
    tos: 'Условия обслуживания',
    term: 'Я соглашаюсь с xxx {0}.'
  }
}

const i18n = new VueI18n({
  locale: 'en',
  messages
})
new Vue({
  i18n,
  data: {
    url: '/term'
  }
}).$mount('#app')

# См. также:

Интерполяция компонента

# Функциональный компонент i18n-n

🆕 Добавлено в версии 8.10+

# Входные параметры:

  • value {number}: обязательный, число для форматирования
  • format {string | NumberFormatOptions}: опционально, форматируемое число или объект с указанными опциями форматирования
  • locale {Locale}: опционально, локализация
  • tag {string}: опционально, по умолчанию span

# Использование:

<div id="app">
  <!-- ... -->
  <i18n-n :value="money" format="currency" tag="label">
    <span v-slot:currency="slotProps" class="font-weight: bold">
      {{ slotProps.currency }}
    <span>
  </i18n-n>
  <!-- ... -->
</div>
var numberFormats = {
  'en-US': {
    currency: {
      style: 'currency',
      currency: 'USD'
    }
  },
  'ru-RU': {
    currency: {
      style: 'currency',
      currency: 'RUB'
    }
  }
}

const i18n = new VueI18n({
  locale: 'en-US',
  numberFormats
})

new Vue({
  i18n,
  data: {
    money: 10234
  }
}).$mount('#app')

# Слоты с ограниченной областью видимости

Функциональный компонент <i18n-n> может принимать различные слоты с ограниченной областью видимости. Список поддерживаемых имён слотов основан на выходных типах Intl.NumberFormat.formatToParts():

  • currency
  • decimal
  • fraction
  • group
  • infinity
  • integer
  • literal
  • minusSign
  • nan
  • plusSign
  • percentSign

Каждый из слотов предоставляет доступ к значениям трёх параметров:

  • [slotName] {FormattedNumberPartType}: параметр с тем же именем, что и имя слота (например, integer)
  • index {Number}: индекс конкретной части в массиве частей числа
  • parts {Array}: массив со всеми отформатированными частями числа

# См. также:

Локализация чисел

# Специальный атрибуты

# place

🆕 Добавлено в версии 7.2+

# Ожидает: {number | string}

Используется при интерполяции компонента для указания индекса при форматировании списком или ключа при именованном форматировании.

Подробнее об использовании можно изучить в разделе по ссылке ниже.

# См. также:

Интерполяция компонента

Последнее обновление: 5/11/2020, 7:03:20 PM
================================================ FILE: docs/ru/guide/component.html ================================================ Локализация на основе компонентов | Vue I18n

# Локализация на основе компонентов

В основном данные для локализации (например, locale,messages, и т.д.) задаются опциями конструктора экземпляра VueI18n и устанавливаются через свойство i18n в корневой экземпляр Vue.

Поэтому можно глобально выполнять переводы, используя методы $t или $tc в корневом экземпляре Vue и любом из компонентов в нём. Но также возможно указывать данные для локализации в каждом компоненте в отдельности, что может быть удобнее благодаря компонентно-ориентированному дизайну.

Пример локализации на основе компонентов:

// Установка локализации в корневой экземпляр Vue
const i18n = new VueI18n({
  locale: 'ru',
  messages: {
    en: {
      message: {
        hello: 'hello world',
        greeting: 'good morning'
      }
    },
    ru: {
      message: {
        hello: 'привет мир',
        greeting: 'доброе утро'
      }
    }
  }
})

// Определение компонента
const Component1 = {
  template: `
    <div class="container">
     <p>Component1 locale messages: {{ $t("message.hello") }}</p>
     <p>Fallback global locale messages: {{ $t("message.greeting") }}</p>
   </div>`,
  i18n: {
    // опция `i18n`, определение данных локализации для компонента
    messages: {
      en: { message: { hello: 'hello component1' } },
      ru: { message: { hello: 'привет component1' } }
    }
  }
}

new Vue({
  i18n,
  components: {
    Component1
  }
}).$mount('#app')

Шаблон:

<div id="app">
  <p>{{ $t("message.hello") }}</p>
  <component1></component1>
</div>

Результат:

<div id="app">
  <p>привет мир</p>
  <div class="container">
    <p>Component1 locale messages: привет component1</p>
    <p>Fallback global locale messages: доброе утро</p>
  </div>
</div>

Если компонент не имеет собственного сообщения для локализации, то в качестве запасного выхода он обратится к глобальным данным для локализации. Компонент использует локаль, установленную в корневом экземпляре (в примере выше установлена: locale: 'ru').

Обратите внимание, по умолчанию при обращении к данным корневой локализации будут генерироваться предупреждения в консоли:

[vue-i18n] Value of key 'message.greeting' is not a string!
[vue-i18n] Fall back to translate the keypath 'message.greeting' with root locale.

Чтобы скрыть эти предупреждения (оставив те, что предупреждают о полном отсутствии перевода для данного ключа) установите опцию silentFallbackWarn: true при инициализации экземпляра VueI18n.

Если необходимо осуществлять перевод, основываясь на локали компонента, то это можно сделать с помощью опции sync: false и locale в настройках i18n.

# Общие сообщения локализации для компонентов

Иногда может потребоваться импортировать сообщения локализации в определённых компонентах, чтобы не обращаться к глобальным сообщениям локализации (например, общие сообщения для определённых функций компонентов).

Для этого можно использовать опцию sharedMessages в свойстве i18n компонента.

Пример использования общих сообщений локализации:

export default {
  en: {
    buttons: {
      save: 'Save'
      // ...
    }
  },
  ru: {
    buttons: {
      save: 'Сохранить'
      // ...
    }
  }
}

Компонент:

import commonMessage from './locales/common' // импорт общих сообщений локализации

export default {
  name: 'ServiceModal',
  template: `
    <div class="modal">
      <div class="body">
        <p>Это хороший сервис</p>
      </div>
      <div class="footer">
        <button type="button">
          {{ $t('buttons.save') }}
        </button>
      </div>
    </div>
  `,
  i18n: {
    messages: { ... },
    sharedMessages: commonMessages
  }
}

Если указаны опции sharedMessages и messages, то их сообщения будут объединены в сообщения локализации в экземпляре VueI18n этого компонента.

# Локализация в функциональных компонентах

При использовании функционального компонента все данные (включая props, children, slots, parent, и т.д.) передаются через context, в котором содержатся все эти атрибуты. Кроме того отсутствует возможность использовать this, поэтому при использовании vue-i18n с функциональными компонентами следует обращаться к $t как к parent.$t, например так:

...
<div>
  <a href="#" target="_blank" rel="noopener noreferrer">
    <img src="" :alt="parent.$t('message.hello')" />
  </a>
</div>
...
Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/guide/datetime.html ================================================ Локализация дат | Vue I18n

# Локализация дат

Поддержка с версии

🆕 7.0+

Можно выполнять локализацию дат по соответствующему формату.

Пример формата для дат:

const dateTimeFormats = {
  'en-US': {
    short: {
      year: 'numeric',
      month: 'short',
      day: 'numeric'
    },
    long: {
      year: 'numeric',
      month: 'short',
      day: 'numeric',
      weekday: 'short',
      hour: 'numeric',
      minute: 'numeric'
    }
  }
}

Как видно выше, можно определять именованный формат даты (например, short, long и т.д.) используя опции ECMA-402 Intl.DateTimeFormat

После этого, для возможности использования данного формата в сообщениях локализации, необходимо задать опцию dateTimeFormats в конструкторе VueI18n:

const i18n = new VueI18n({
  dateTimeFormats
})

new Vue({
  i18n
}).$mount('#app')

Шаблон:

<div id="app">
  <p>{{ $d(new Date(), 'short') }}</p>
  <p>{{ $d(new Date(), 'long', 'ru-RU') }}</p>
</div>

Результат:

<div id="app">
  <p>Apr 19, 2017</p>
  <p>2017年4月19日(水) 午前2:19</p>
</div>
Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/guide/directive.html ================================================ Пользовательская директива | Vue I18n

# Пользовательская директива

Поддержка с версии

🆕 7.3+

Переводы можно осуществлять не только используя пользовательскую директиву v-t, но и с помощью метода $t.

# Строковый синтаксис

Можно передавать ключ сообщения локализации строкой.

JavaScript:

new Vue({
  i18n: new VueI18n({
    locale: 'en',
    messages: {
      en: { hello: 'hi there!' },
      ru: { hello: 'привет всем!' }
    }
  }),
  data: { path: 'hello' }
}).$mount('#string-syntax')

Шаблон:

<div id="string-syntax">
  <!-- строковый литерал -->
  <p v-t="'hello'"></p>
  <!-- привязка пути к сообщению из данных -->
  <p v-t="path"></p>
</div>

Результат:

<div id="string-syntax">
  <p>привет всем!</p>
  <p>привет всем!</p>
</div>

# Объектный синтаксис

Можно использовать объектный синтаксис.

JavaScript:

new Vue({
  i18n: new VueI18n({
    locale: 'en',
    messages: {
      en: { hello: 'hi {name}!' },
      ru: { hello: 'привет {name}!' }
    }
  }),
  computed: {
    nickName() {
      return 'kazupon'
    }
  },
  data: { path: 'hello' }
}).$mount('#object-syntax')

Шаблон:

<div id="object-syntax">
  <!-- литерал -->
  <p v-t="{ path: 'hello', locale: 'ru', args: { name: 'kazupon' } }"></p>
  <!-- привязка к данным -->
  <p v-t="{ path: path, args: { name: nickName } }"></p>
</div>

Результат:

<div id="object-syntax">
  <p>привет、kazupon!</p>
  <p>hi kazupon!</p>
</div>

# Использование с transition

Поддержка с версии

🆕 8.7+

При использовании директивы v-t на элементе внутри компонента <transition>, можно заметить как переведённое сообщение исчезает во время анимации перехода. Это поведение связано с реализацией самого компонента <transition> — все директивы в исчезающем элементе внутри компонента <transition> должны быть уничтожены до начала анимации. Это может привести к мерцанию содержимого на коротких анимациях, но наиболее заметно при длинных анимациях переходов.

Чтобы сохранить содержимое директивы во время анимации перехода, необходимо добавить модификатор .preserve при определении директивы v-t.

Javascript:

new Vue({
  i18n: new VueI18n({
    locale: 'en',
    messages: {
      en: { preserve: 'with preserve' }
    }
  }),
  data: { toggle: true }
}).$mount('#in-transitions')

Шаблон:

<div id="in-transitions">
  <transition name="fade">
    <span v-if="toggle" v-t.preserve="'preserve'"></span>
  </transition>
  <button @click="toggle = !toggle">Toggle</button>
</div>

Также можно глобально установить настройку preserveDirectiveContent в экземпляре VueI18n, что повлияет на все директивы v-t без добавления модификатора к ним.

JavaScript:

new Vue({
  i18n: new VueI18n({
    locale: 'en',
    messages: {
      en: { preserve: 'with preserve' }
    },
    preserveDirectiveContent: true
  }),
  data: { toggle: true }
}).$mount('#in-transitions')

Шаблон:

<div id="in-transitions">
  <transition name="fade">
    <span v-if="toggle" v-t="'preserve'"></span>
  </transition>
  <button @click="toggle = !toggle">Toggle</button>
</div>

Подробнее о примерах выше можно изучить здесь

# $t или v-t

# $t

$t — это метод, добавленный в экземпляр Vue. У него следующие плюсы и минусы:

# Плюсы

Предоставляет гибкость в использовании синтаксиса фигурных скобок {{}} в шаблонах, а также применять в вычисляемых свойствах и методах экземпляра Vue.

# Минусы

$t выполняется каждый раз когда происходит перерисовка, поэтому у него есть расходы на осуществление перевода.

# v-t

v-t — пользовательская директива. У неё следующие плюсы и минусы:

# Плюсы

v-t имеет лучшую производительность в сравнении с методом $t, благодаря кэшу в пользовательской директиве после выполнения перевода. Также можно реализовать предварительный перевод с помощью модуля для компилятора Vue, который предоставляет плагин vue-i18n-extensions.

Таким образом, можно достичь большей оптимизации производительности.

# Минусы

v-t нельзя использовать также гибко, как $t, и это добавляет сложности. Перевод с помощью v-t вставляется в textContent элемента. Также, при использовании рендеринга на стороне сервера необходимо установить пользовательскую директиву через опцию directives функции createRenderer.

Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/guide/fallback.html ================================================ Запасная локализация | Vue I18n

# Запасная локализация

Вкратце: указывайте fallbackLocale: '<lang>' для определения языка, который будет использоваться, если нет перевода в выбранной локализации.

# Неявное определение запасных локализаций при использовании локалей

Если locale содержит территорию и опционально диалект, то неявно будут определены автоматически запасные локали.

Например для de-DE-bavarian в качестве запасных будут считаться следующие:

  1. de-DE-bavarian
  2. de-DE
  3. de

Для отключения автоматического определения запасных локалей укажите символ !, например de-DE!

# Явное определение одной локали запасной локализации

Иногда не все ключи сообщений переведены на другие языки. В примере ниже, сообщение для ключа hello доступно в английской локали, но отсутствует в русской:

const messages = {
  en: {
    hello: 'Hello, world!'
  },
  ru: {
    // упс, не все переведено
  }
}

Если хочется использовать сообщения локализации из en, когда перевод отсутствует в нужной локализации, то следует указать опцию fallbackLocale в конструкторе VueI18n:

const i18n = new VueI18n({
  locale: 'ru',
  fallbackLocale: 'en',
  messages
})

Шаблон:

<p>{{ $t('hello') }}</p>

Результат:

<p>Hello, world!</p>

По умолчанию, если использовались сообщения запасной локализации из fallbackLocale, то в консоли будут выведены соответствующие предупреждения:

[vue-i18n] Value of key 'hello' is not a string!
[vue-i18n] Fall back to translate the keypath 'hello' with 'en' locale.

Чтобы скрыть такие предупреждения (оставив при этом те, в случаях полного отсутствия переводов для данного ключа) установите silentFallbackWarn: true при инициализации экземпляра VueI18n.

# Явное определение запасной локали с помощью массива

Можно указать более одной запасной локализации с помощью массива. Например:

fallbackLocale: ['ru', 'en'],

# Явное определение запасной локали с помощью объекта

Более сложный алгоритм принятия решений для определения запасной локали можно реализовать с помощью карты принятия решений в виде объекта.

Например для подобного объекта:

fallbackLocale: {
  /* 1 */ 'de-CH':   ['fr', 'it'],
  /* 2 */ 'zh-Hant': ['zh-Hans'],
  /* 3 */ 'es-CL':   ['es-AR'],
  /* 4 */ 'es':      ['en-GB'],
  /* 5 */ 'pt':      ['es-AR'],
  /* 6 */ 'default': ['en', 'ru']
},

Будут следующие цепочки выбора запасной локали:

Локаль Цепочка выбора
'de-CH' de-CH > fr > it > en > ru
'de' de > en > ru
'zh-Hant' zh-Hant > zh-Hans > zh > en > ru
'es-SP' es-SP > es > en-GB > en > ru
'es-SP!' es-SP > en > ru
'fr' fr > en > ru
'pt-BR' pt-BR > pt > es-AR > es > en-GB > en > ru
'es-CL' es-CL > es-AR > es > en-GB > en > ru

# Резервная интерполяция

Вкратце: установите formatFallbackMessages: true чтобы выполнять интерполяции шаблона по ключам перевода, когда в выбранном языке отсутствует данный ключ для перевода.

Так как ключи переводов являются строками, то можно использовать само сообщение в качестве ключа (для определённого языка). Например:

const messages = {
  ru: {
    'Hello, world!': 'Привет мир!'
  }
}

Это может быть полезным, потому что не нужно будет указывать перевод для строки "Hello, world!" в английской локализации.

Фактически, можно указывать даже параметры в ключе. Вместе с formatFallbackMessages: true это позволит опустить создание шаблонов для «базового» языка; потому что его ключи уже находятся в шаблоне.

const messages = {
  ru: {
    'Hello {name}': 'Здравствуйте {name}'
  }
}

const i18n = new VueI18n({
  locale: 'ru',
  fallbackLocale: 'en',
  formatFallbackMessages: true,
  messages
})

Шаблон:

<p>{{ $t('Hello {name}', { name: 'John' }}) }}</p>
<p>{{ $t('The weather today is {condition}!', { condition: 'sunny' }) }}</p>

Результат:

<p>Здравствуйте, John</p>
<p>The weather today is sunny!</p>
Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/guide/formatting.html ================================================ Формат сообщений локализации | Vue I18n

# Формат сообщений локализации

# Именованный формат

Сообщения локализации:

const messages = {
  en: {
    message: {
      hello: '{msg} world'
    }
  },
  ru: {
    message: {
      hello: '{msg} мир'
    }
  }
}

Шаблон:

<p>{{ $t('message.hello', { msg: 'привет' }) }}</p>

Результат:

<p>привет мир</p>

# Формат списков

Сообщения локализации:

const messages = {
  en: {
    message: {
      hello: '{0} world'
    }
  },
  ru: {
    message: {
      hello: '{0} мир'
    }
  }
}

Шаблон:

<p>{{ $t('message.hello', ['привееет']) }}</p>

Результат:

<p>привееет мир</p>

Форматирование списков также принимает объекты, соответствующие по структуре массиву:

<p>{{ $t('message.hello', {'0': 'привееет'}) }}</p>

Результат:

<p>привееет мир</p>

# HTML формат

Обратите внимание

⚠️ Динамическая локализация произвольного HTML на вебсайте очень опасна, потому что легко может привести к XSS-уязвимостям. Используйте HTML-интерполяцию только для доверенного контента и никогда для пользовательского.

Рекомендуем в таких случаях использовать возможности интерполяции компонента.

Обратите внимание

🆕 Добавлено в версии 8.11+

Можно управлять использованием HTML форматирования. Для подробностей см. опцию конструктора warnHtmlInMessage и свойства API.

Иногда требуется отобразить сообщение локализации HTML-кодом, а не статической строкой.

const messages = {
  en: {
    message: {
      hello: 'hello <br> world'
    }
  },
  ru: {
    message: {
      hello: 'привет <br> мир'
    }
  }
}

Шаблон:

<p v-html="$t('message.hello')"></p>

Результат (вместо отформатированного сообщения)

<p>
  привет
  <!-- <br> существует, но отрисован как html, а не строкой -->
  мир
</p>

# Формат ruby on rails i18n

Сообщения локализации:

const messages = {
  en: {
    message: {
      hello: '%{msg} world'
    }
  },
  ru: {
    message: {
      hello: '%{msg} мир'
    }
  }
}

Шаблон:

<p>{{ $t('message.hello', { msg: 'привет' }) }}</p>

Результат:

<p>привет мир</p>

# Пользовательский формат

Иногда может потребоваться реализовать локализацию для собственного формата сообщений (например, использовать синтаксиса сообщений ICU).

Реализовать это можно с помощью специального пользовательского метода форматирования, который должен реализовать интерфейс Formatter.

Пример пользовательского метода форматирования с синтаксисом класса ES2015:

// Реализация пользовательского Formatter
class CustomFormatter {
  constructor(options) {
    // ...
  }

  //
  // interpolate
  //
  // @param {string} message
  //   строка или список или именованный формат
  //   напр.
  //   - именованный формат: 'Hi {name}'
  //   - формат списка: 'Hi {0}'
  //
  // @param {Object | Array} values
  //   значения интерполяции `message`.
  //   переданные значения с `$t`, `$tc` и функциональным компонентом `i18n`.
  //   напр.
  //   - $t('hello', { name: 'Alex' }) -> переданные значения: Object `{ name: 'Alex' }`
  //   - $t('hello', ['Alex']) -> переданные значения: Array `['Alex']`
  //   - функциональный компонент `i18n` (интерполяция в компоненте)
  //     <i18n path="hello">
  //       <p>Alex</p>
  //       <p>how are you?</p>
  //     </i18n>
  //     -> переданные значения: Array (included VNode):
  //        `[VNode{ tag: 'p', text: 'Alex', ...}, VNode{ tag: 'p', text: 'how are you?', ...}]`
  //
  // @return {Array<any>}
  //   интерполированные значения. Они необходимы чтобы вернуть следующее:
  //   - массив строк, когда используется `$t` или `$tc`.
  //   - массив, включая объект VNode, когда используется функциональный компонент `i18n`.
  //
  interpolate(message, values) {
    // реализация логики интерполяции
    // ...

    // возвращаем интерполированный массив
    return ['resolved message string']
  }
}

// Регистрация через опцию `formatter`
const i18n = new VueI18n({
  locale: 'en-US',
  formatter: new CustomFormatter(/* опции конструктора */),
  messages: {
    'en-US': {
      // ...
    },
    'ru-RU': {
      // ...
    }
    // ...
  }
})

// Запускаем приложение!
new Vue({ i18n }).$mount('#app')

Также посмотрите официальный пример пользовательского метода форматирования.

Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/guide/hot-reload.html ================================================ Горячая перезагрузка переводов | Vue I18n

# Горячая перезагрузка переводов

С помощью функции Webpack для горячей перезагрузки модулей (HMR) можно отслеживать изменения в файлах локализации и осуществлять их горячую перезагрузку в приложении.

# Простой пример

Для статичного набора локалей, можно явно указать горячую перезагрузку этих переводов:

import Vue from "vue"
import VueI18n from "vue-i18n"
import en from './en'
import ru from './ru'

const messages = {
  en,
  ru
}

// Экземпляр VueI18n
const i18n = new VueI18n({
  locale: 'en',
  messages
})

// Запускаем приложение
const app = new Vue({
  i18n
  // ...
}).$mount('#app')

// Добавляем горячую перезагрузку сообщений локализации
if (module.hot) {
  module.hot.accept(['./en', './ru'], function() {
    i18n.setLocaleMessage('en', require('./en').default)
    i18n.setLocaleMessage('ru', require('./ru').default)
    // Или добавляем горячую перезагрузку через свойство $i18n
    // app.$i18n.setLocaleMessage('en', require('./en').default)
    // app.$i18n.setLocaleMessage('ru', require('./ru').default)
  })
}

# Продвинутый пример

Если требуется поддержка изменяющегося набор переводов, можно реализовать горячую перезагрузку для всех локалей динамически через require.context:

import Vue from "vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);

// Загрузка всех локалей и сохранение контекста
function loadMessages() {
  const context = require.context("./locales", true, /[a-z0-9-_]+\.json$/i);

  const messages = context
    .keys()
    .map((key) => ({ key, locale: key.match(/[a-z0-9-_]+/i)[0] }))
    .reduce(
      (messages, { key, locale }) => ({
        ...messages,
        [locale]: context(key),
      }),
      {}
    );

  return { context, messages };
}

const { context, messages } = loadMessages();

// Экземпляр VueI18n
const i18n = new VueI18n({
  locale: "en",
  messages,
});

// Запускаем приложение
const app = new Vue({
  i18n,
  // ...
}).$mount('#app');

// Добавляем горячую перезагрузку сообщений локализации
if (module.hot) {
  module.hot.accept(context.id, () => {
    const { messages: newMessages } = loadMessages();

    Object.keys(newMessages)
      .filter((locale) => messages[locale] !== newMessages[locale])
      .forEach((locale) => {
        messages[locale] = newMessages[locale];
        i18n.setLocaleMessage(locale, messages[locale]);
      });
  });
}
Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/guide/interpolation.html ================================================ Интерполяция компонента | Vue I18n

# Интерполяция компонента

# Базовое использование

Поддержка с версии

🆕 7.0+

Иногда требуется перевести сообщения в которых есть HTML теги или компоненты. Например:

<p>I accept xxx <a href="/term">Terms of Service Agreement</a></p>

Для такого сообщения, если хотим использовать $t, то, вероятно, попробуем достичь скомпоновав из следующих сообщений локализации:

const messages = {
  en: {
    term1: "I Accept xxx's",
    term2: 'Terms of Service Agreement'
  }
}

И в итоге шаблон станет выглядеть так:

<p>{{ $t('term1') }}<a href="/term">{{ $t('term2') }}</a></p>

Результат:

<p>I accept xxx <a href="/term">Terms of Service Agreement</a></p>

Это выглядит очень громоздко, но если перенести тег <a> в сообщение локализации, то добавится вероятность XSS-уязвимости из-за применения v-html="$t('term')".

Этого можно избежать воспользовавшись функциональным компонентом i18n. Например:

<div id="app">
  <!-- ... -->
  <i18n path="term" tag="label" for="tos">
    <a :href="url" target="_blank">{{ $t('tos') }}</a>
  </i18n>
  <!-- ... -->
</div>
const messages = {
  en: {
    tos: 'Term of Service',
    term: 'I accept xxx {0}.'
  },
  ru: {
    tos: 'Условия обслуживания',
    term: 'Я соглашаюсь с xxx {0}.'
  }
}

const i18n = new VueI18n({
  locale: 'en',
  messages
})
new Vue({
  i18n,
  data: {
    url: '/term'
  }
}).$mount('#app')

Результат:

<div id="app">
  <!-- ... -->
  <label for="tos">
    I accept xxx <a href="/term" target="_blank">Term of Service</a>.
  </label>
  <!-- ... -->
</div>

Подробнее о примере можно изучить здесь

Потомки функционального компонента i18n интерполируют сообщения локализации по входному параметру path. В примере выше,

<a :href="url" target="_blank">{{ $t('tos') }}</a>

интерполируется с сообщением локализации term.

В примере выше интерполяция компонента использует формат в виде списка. Потомки функционального компонента i18n интерполируются по порядку их появления.

# Использование синтаксиса слотов

Поддержка с версии

🆕 8.14+

Гораздо удобнее использовать синтаксис именованных слотов. Например:

<div id="app">
  <!-- ... -->
  <i18n path="info" tag="p">
    <template v-slot:limit>
      <span>{{ changeLimit }}</span>
    </template>
    <template v-slot:action>
      <a :href="changeUrl">{{ $t('change') }}</a>
    </template>
  </i18n>
  <!-- ... -->
</div>
const messages = {
  en: {
    info: 'You can {action} until {limit} minutes from departure.',
    change: 'change your flight',
    refund: 'refund the ticket'
  }
}

const i18n = new VueI18n({
  locale: 'en',
  messages
})

new Vue({
  i18n,
  data: {
    changeUrl: '/change',
    refundUrl: '/refund',
    changeLimit: 15,
    refundLimit: 30
  }
}).$mount('#app')

Результат:

<div id="app">
  <!-- ... -->
  <p>
    You can <a href="/change">change your flight</a> until
    <span>15</span> minutes from departure.
  </p>
  <!-- ... -->
</div>

С версии Vue 2.6 можно использовать сокращённый синтаксис слотов в шаблонах:

<div id="app">
  <!-- ... -->
  <i18n path="info" tag="p">
    <template #limit>
      <span>{{ changeLimit }}</span>
    </template>
    <template #action>
      <a :href="changeUrl">{{ $t('change') }}</a>
    </template>
  </i18n>
  <!-- ... -->
</div>

Ограничение

⚠️ В компоненте i18n входные параметры слота не поддерживаются.

# Использование синтаксиса places

Внимание!

В следующей мажорной версии входные параметры place и places будут объявлены устаревшими. Рекомендуем использовать синтаксис слотов.

Поддержка с версии

🆕 7.2+

Обратите внимание

⚠️ В компоненте i18n содержимое, состоящее только из пробелов, будет опущено.

Именованное форматирование поддерживается с помощью атрибута place. Например:

<div id="app">
  <!-- ... -->
  <i18n path="info" tag="p">
    <span place="limit">{{ changeLimit }}</span>
    <a place="action" :href="changeUrl">{{ $t('change') }}</a>
  </i18n>
  <!-- ... -->
</div>
const messages = {
  en: {
    info: 'You can {action} until {limit} minutes from departure.',
    change: 'change your flight',
    refund: 'refund the ticket'
  }
}

const i18n = new VueI18n({
  locale: 'en',
  messages
})
new Vue({
  i18n,
  data: {
    changeUrl: '/change',
    refundUrl: '/refund',
    changeLimit: 15,
    refundLimit: 30
  }
}).$mount('#app')

Результат:

<div id="app">
  <!-- ... -->
  <p>
    You can <a href="/change">change your flight</a> until
    <span>15</span> minutes from departure.
  </p>
  <!-- ... -->
</div>

Обратите внимание

⚠️ Для использования именованного форматирования все потомки компонента i18n должны иметь установленный атрибут place. В противном случае будет использовано форматирование списком.

Если всё же необходимо интерполировать текстовое содержимое с помощью именованного форматирования, можно определить свойство places на компоненте i18n. Например:

<div id="app">
  <!-- ... -->
  <i18n path="info" tag="p" :places="{ limit: refundLimit }">
    <a place="action" :href="refundUrl">{{ $t('refund') }}</a>
  </i18n>
  <!-- ... -->
</div>

Результат:

<div id="app">
  <!-- ... -->
  <p>
    You can <a href="/refund">refund your ticket</a> until 30 minutes from
    departure.
  </p>
  <!-- ... -->
</div>
Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/guide/lazy-loading.html ================================================ Ленивая загрузка переводов | Vue I18n

# Ленивая загрузка переводов

Одновременная загрузка всех файлов переводов может быть излишней и ненужной.

Ленивая или асинхронная загрузка файлов переводов очень просто реализуется при использовании Webpack.

Предположим, что у нас есть каталог проекта следующей структуры:

our-cool-project
-dist
-src
--routes
--store
--setup
---i18n-setup.js
--lang
---en.js
---it.js

В каталоге lang располагаются все файлы переводов. В каталоге setup сгруппированы различные файлы настроек, например настройки i18n, регистрация глобальных компонентов, инициализации плагинов и другое.

// i18n-setup.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import messages from '@/lang/en'
import axios from 'axios'

Vue.use(VueI18n)

export const i18n = new VueI18n({
  locale: 'en', // установка локализации
  fallbackLocale: 'en',
  messages // установка сообщений локализации
})

const loadedLanguages = ['en'] // список локализаций, которые пред-загружены

function setI18nLanguage(lang) {
  i18n.locale = lang
  axios.defaults.headers.common['Accept-Language'] = lang
  document.querySelector('html').setAttribute('lang', lang)
  return lang
}

export function loadLanguageAsync(lang) {
  // Если локализация та же
  if (i18n.locale === lang) {
    return Promise.resolve(setI18nLanguage(lang))
  }

  // Если локализация уже была загружена
  if (loadedLanguages.includes(lang)) {
    return Promise.resolve(setI18nLanguage(lang))
  }

  // Если локализация ещё не была загружена
  return import(
    /* webpackChunkName: "lang-[request]" */ `@/i18n/messages/${lang}.js`
  ).then(messages => {
    i18n.setLocaleMessage(lang, messages.default)
    loadedLanguages.push(lang)
    return setI18nLanguage(lang)
  })
}

Для начала создаём новый экземпляр VueI18n как обычно. Затем определяем массив loadedLanguages в котором будем хранить список загруженных языков. Далее создаём функцию setI18nLanguage, которая будет переключать локализацию в экземпляре vueI18n, axios и где ещё это необходимо.

Функция loadLanguageAsync будет использоваться для изменения языка. Загрузка новых файлов осуществляется функцией import, которую предоставляет Webpack и позволяет загружать файлы динамически, а поскольку она возвращает Promise, то можем легко дождаться окончания загрузки.

Подробнее о динамических импортах можно изучить в документации Webpack.

Использовать loadLanguageAsync очень просто. Например, в хуке beforeEach vue-router.

router.beforeEach((to, from, next) => {
  const lang = to.params.lang
  loadLanguageAsync(lang).then(() => next())
})

Можно доработать реализацию, например добавив проверку поддерживается ли переданный lang или нет и вызывать reject чтобы отловить подобные случаи в хуке beforeEach и остановить навигацию.

Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/guide/locale.html ================================================ Переключение локализации | Vue I18n

# Переключение локализации

Обычно используют корневой экземпляр Vue в качестве точки истины, а все дочерние компоненты используют свойство locale от класса VueI18n передаваемого по ссылке.

Иногда может потребоваться динамически переключать локализацию. Для этого нужно изменить значение свойства locale экземпляра VueI18n.

const i18n = new VueI18n({
  locale: 'ru', // устанавливаем локализацию по умолчанию
  ...
})

// Создаём корневой экземпляр Vue
new Vue({
  i18n,
  ...
}).$mount('#app')

// Переключаем на другую локализацию
i18n.locale = 'en'

Каждый компонент содержит экземпляр VueI18n, ссылающийся на свойство $i18n, которое также можно использовать для переключения локализации.

Пример:

<template>
  <div class="locale-changer">
    <select v-model="$i18n.locale">
      <option v-for="(lang, i) in langs" :key="`Lang${i}`" :value="lang">
        {{ lang }}
      </option>
    </select>
  </div>
</template>

<script>
  export default {
    name: 'locale-changer',
    data() {
      return { langs: ['ru', 'en'] }
    }
  }
</script>

Обратите внимание

⚠️ Изменение локализации игнорируется компонентами с опцией sync: false.

Компонент vs. корневая область видимости

⚠️ Изменение $i18n.locale внутри компонента не приводит к изменению корневой локализации. Если вы полагаетесь на корневую локализацию, например, при использовании корневой запасной локализации, используйте $root.$i18n.locale вместо $i18n.locale.

Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/guide/messages.html ================================================ Синтаксис сообщений локализации | Vue I18n

# Синтаксис сообщений локализации

# Структура

Синтаксис сообщений локализации:

// As Flowtype definition, Locale Messages syntax like BNF annotation
type LocaleMessages = { [key: Locale]: LocaleMessageObject }
type LocaleMessageObject = { [key: Path]: LocaleMessage }
type LocaleMessageArray = LocaleMessage[]
type LocaleMessage = string | LocaleMessageObject | LocaleMessageArray
type Locale = string
type Path = string

Используя синтаксис выше, можно создать следующую структуру сообщений локализации:

{
  // локализация 'en'
  "en": {
    "key1": "это сообщение 1", // обычное использование
    "nested": {
      // вложенное
      "message1": "это вложенное сообщение 1"
    },
    "errors": [
      // массив
      "это сообщение кода ошибки 0",
      {
        // объект в массиве
        "internal1": "это внутреннее сообщение кода ошибки 1"
      },
      [
        // массив в массиве
        "это вложенный массив ошибки 1"
      ]
    ]
  },
  // локализация 'ru'
  "ru": {
    // ...
  }
}

Для такой структуры сообщений локализации, можно переводить сообщения используя ключи:

<div id="app">
  <!-- обычное использование -->
  <p>{{ $t('key1') }}</p>
  <!-- вложенное -->
  <p>{{ $t('nested.message1') }}</p>
  <!-- массив -->
  <p>{{ $t('errors[0]') }}</p>
  <!-- объект в массиве -->
  <p>{{ $t('errors[1].internal1') }}</p>
  <!-- массив в массиве -->
  <p>{{ $t('errors[2][0]') }}</p>
</div>

Результат:

<div id="app">
  <!-- обычное использование -->
  <p>это сообщение 1</p>
  <!-- вложенное -->
  <p>это вложенное сообщение 1</p>
  <!-- массив -->
  <p>это сообщение кода ошибки 0</p>
  <!-- объект в массиве -->
  <p>это внутреннее сообщение кода ошибки 1</p>
  <!-- массив в массиве -->
  <p>это вложенный массив ошибки 1</p>
</div>

# Связанные сообщения локализации

Когда есть ключ с сообщением перевода, которое в точности повторяется в сообщении по другому ключу, то вместо дублирования можно поставить ссылку на него. Для этого к его содержимому нужно добавить префикс @: после которого указать полное имя ключа к сообщению перевода, включая пространство имён, к которому делаем ссылку.

Сообщения локализации:

const messages = {
  en: {
    message: {
      the_world: 'the world',
      dio: 'DIO:',
      linked: '@:message.dio @:message.the_world !!!!'
    }
  }
}

Шаблон:

<p>{{ $t('message.linked') }}</p>

Результат:

<p>DIO: the world !!!!</p>

# Форматирование связанных сообщений локализации

Если важен регистр символов в переводе, то можно управлять регистром связанного сообщения локализации. Связанные сообщения можно отформатировать используя модификатор @.modifier:key

Доступны следующие модификаторы:

  • upper: Форматирование в верхний регистр всех символов в связанном сообщении.
  • lower: Форматирование в нижний регистр всех символов в связанном сообщении.
  • capitalize: Форматирование заглавной первой буквы в связанном сообщении.

Сообщения локализации:

const messages = {
  en: {
    message: {
      homeAddress: 'Home address',
      missingHomeAddress: 'Please provide @.lower:message.homeAddress'
    }
  },
  ru: {
    message: {
      homeAddress: 'Домашний адрес',
      missingHomeAddress: 'Пожалуйста укажите @.lower:message.homeAddress'
    }
  }
}
<label>{{ $t('message.homeAddress') }}</label>

<p class="error">{{ $t('message.missingHomeAddress') }}</p>

Результат:

<label>Домашний адрес</label>

<p class="error">Пожалуйста укажите домашний адрес</p>

При необходимости можно добавлять новые модификаторы или перезаписывать существующие через опцию modifiers в конструкторе VueI18n.

const i18n = new VueI18n({
  locale: 'ru',
  modifiers: {
    // Добавление нового модификатора
    snakeCase: str => str.split(' ').join('-')
  },
  messages: {
    // ...
  },
})

# Группировка с помощью скобок

Ключ связанного сообщения также можно указывать в виде @:(message.foo.bar.baz), где ссылка на другой ключ перевода обрамляется в скобки ().

Подобное может потребоваться, если за ссылкой на другое сообщение @:message.something требуется поставить точку ., которая в противном случае считалась бы частью ссылки.

Сообщения локализации:

const messages = {
  en: {
    message: {
      dio: 'DIO',
      linked: "There's a reason, you lost, @:(message.dio)."
    }
  },
  ru: {
    message: {
      dio: 'DIO',
      linked: "Есть причина по которой ты проиграл, @:(message.dio)."
    }
  }
}

Шаблон:

<p>{{ $t('message.linked') }}</p>

Результат:

<p>There's a reason, you lost, DIO.</p>
Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/guide/number.html ================================================ Локализация чисел | Vue I18n

# Локализация чисел

Поддержка с версии

🆕 7.0+

Можно выполнять локализацию чисел по соответствующему формату.

Пример формата для чисел:

const numberFormats = {
  'en-US': {
    currency: {
      style: 'currency',
      currency: 'USD'
    }
  },
  'ru-RU': {
    currency: {
      style: 'currency',
      currency: 'RUB',
      currencyDisplay: 'symbol'
    }
  }
}

Как указано выше, можно задать числовые форматы (например, currency для валюты) используя опции ECMA-402 Intl.NumberFormat.

После этого, для возможности использования данного формата в сообщениях локализации, необходимо задать опцию numberFormats конструктора VueI18n:

const i18n = new VueI18n({
  numberFormats
})

new Vue({
  i18n
}).$mount('#app')

Шаблон:

<div id="app">
  <p>{{ $n(100, 'currency') }}</p>
  <p>{{ $n(100, 'currency', 'ru-RU') }}</p>
</div>

Результат:

<div id="app">
  <p>$100.00</p>
  <p>100,00 ₽</p>
</div>

# Пользовательское форматирование

Поддержка с версии

🆕 8.10+

Метод $n возвращает результат в виде строки с полностью отформатированным числом, которую можно использовать лишь целиком. В случаях, когда нужно стилизовать некоторую часть отформатированного числа (например, дробную часть), $n будет недостаточно. В таких случаях необходимо использовать функциональный компонент <i18n-n>.

При минимальном наборе свойств <i18n-n> генерирует тот же результат, что и $n обернутый в сконфигурированный DOM-элемент.

Шаблон:

<div id="app">
  <i18n-n :value="100"></i18n-n>
  <i18n-n :value="100" format="currency"></i18n-n>
  <i18n-n :value="100" format="currency" locale="ru-RU"></i18n-n>
</div>

Результат:

<div id="app">
  <span>100</span>
  <span>$100.00</span>
  <span>100,00 ₽</span>
</div>

Но по-настоящему оценить удобство компонента можно лишь тогда, когда он применяется с использованием слотов с ограниченной областью видимости.

Допустим, есть требование выводить целую часть числа полужирным шрифтом. Реализовать это можно с помощью слота integer:

<i18n-n :value="100" format="currency">
  <span v-slot:integer="slotProps" styles="font-weight: bold">
    {{ slotProps.integer }}
  </span>
</i18n-n>

Результат:

<span>$<span styles="font-weight: bold">100</span>.00</span>

Можно использовать несколько слотов одновременно:

<i18n-n :value="1234" :format="{ key: 'currency', currency: 'EUR' }">
  <span v-slot:currency="slotProps" styles="color: green">
    {{ slotProps.currency }}
  </span>
  <span v-slot:integer="slotProps" styles="font-weight: bold">
    {{ slotProps.integer }}
  </span>
  <span v-slot:group="slotProps" styles="font-weight: bold">
    {{ slotProps.group }}
  </span>
  <span v-slot:fraction="slotProps" styles="font-size: small">
    {{ slotProps.fraction }}
  </span>
</i18n-n>

(Полученный HTML ниже отформатирован для лучшей читаемости)

<span>
  <span styles="color: green"></span>
  <span styles="font-weight: bold">1</span>
  <span styles="font-weight: bold">,</span>
  <span styles="font-weight: bold">234</span>
  .
  <span styles="font-size: small">00</span>
</span>

Полный список поддерживаемых слотов, а также другие свойства <i18n-n> можно найти на странице справочника API.

Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/guide/pluralization.html ================================================ Плюрализация | Vue I18n

# Плюрализация

Для переводимых сообщений есть возможность использовать плюрализацию. Для этого необходимо указывать строки переводов для различных случаев через разделитель |.

В шаблоне в таких случаях необходимо использовать метод $tc() вместо $t().

Сообщения локализации:

const messages = {
  en: {
    car: 'car | cars',
    apple: 'no apples | one apple | {count} apples'
  },
  ru: {
    car: 'машина | машины',
    apple: 'нет яблок | одно яблоко | {count} яблок'
  }
}

Шаблон:

<p>{{ $tc('car', 1) }}</p>
<p>{{ $tc('car', 2) }}</p>

<p>{{ $tc('apple', 0) }}</p>
<p>{{ $tc('apple', 1) }}</p>
<p>{{ $tc('apple', 10, { count: 10 }) }}</p>

Результат:

<p>машина</p>
<p>машины</p>

<p>нет яблок</p>
<p>одно яблоко</p>
<p>10 яблок</p>

# Аргумент для доступа к числу

Нет необходимости явно передавать число для плюрализации. В сообщениях локализации число доступно через именованные аргументы {count} и/или {n}. При желании их можно переопределить.

Сообщения локализации:

const messages = {
  en: {
    apple: 'no apples | one apple | {count} apples',
    banana: 'no bananas | {n} banana | {n} bananas'
  },
  ru: {
    apple: 'нет яблок | одно яблоко | {count} яблок',
    banana: 'нет бананов | {n} банан | {n} бананов'
  }
}

Шаблон:

<p>{{ $tc('apple', 10, { count: 10 }) }}</p>
<p>{{ $tc('apple', 10) }}</p>

<p>{{ $tc('banana', 1, { n: 1 }) }}</p>
<p>{{ $tc('banana', 1) }}</p>
<p>{{ $tc('banana', 100, { n: 'слишком много' }) }}</p>

Результат:

<p>10 яблок</p>
<p>10 яблок</p>

<p>1 банан</p>
<p>1 банан</p>
<p>слишком много бананов</p>

# Пользовательская плюрализация

Стандартная реализация плюрализации может быть не применима ко всем языкам (к примеру, в Славянских языках другие правила множественности).

Можно указать собственную реализацию переопределив функцию VueI18n.prototype.getChoiceIndex.

Очень упрощённый пример правил для Славянских языков (Русский, Украинский и другие):

const defaultImpl = VueI18n.prototype.getChoiceIndex

/**
 * @param choice {number} индекс выбора, переданный в $tc: `$tc('path.to.rule', choiceIndex)`
 * @param choicesLength {number} общее количество доступных вариантов
 * @returns финальный индекс для выбора соответственного варианта слова
 **/
VueI18n.prototype.getChoiceIndex = function(choice, choicesLength) {
  // this === экземпляр VueI18n, поэтому свойство locale также здесь существует
  if (this.locale !== 'ru') {
    // возвращаемся к реализации по умолчанию
    return defaultImpl.apply(this, arguments)
  }

  if (choice === 0) {
    return 0
  }

  const teen = choice > 10 && choice < 20
  const endsWithOne = choice % 10 === 1

  if (!teen && endsWithOne) {
    return 1
  }

  if (!teen && choice % 10 >= 2 && choice % 10 <= 4) {
    return 2
  }

  return choicesLength < 4 ? 2 : 3
}

Такая реализация позволит использовать:

const messages = {
  ru: {
    car: '0 машин | {n} машина | {n} машины | {n} машин',
    banana: 'нет бананов | {n} банан | {n} банана | {n} бананов'
  }
}

Для такого формата вариантов 0 вещей | 1 вещь | несколько вещей | множество вещей.

В шаблоне, по-прежнему, необходимо использовать $tc() вместо $t():

<p>{{ $tc('car', 1) }}</p>
<p>{{ $tc('car', 2) }}</p>
<p>{{ $tc('car', 4) }}</p>
<p>{{ $tc('car', 12) }}</p>
<p>{{ $tc('car', 21) }}</p>

<p>{{ $tc('banana', 0) }}</p>
<p>{{ $tc('banana', 4) }}</p>
<p>{{ $tc('banana', 11) }}</p>
<p>{{ $tc('banana', 31) }}</p>

Результат:

<p>1 машина</p>
<p>2 машины</p>
<p>4 машины</p>
<p>12 машин</p>
<p>21 машина</p>

<p>нет бананов</p>
<p>4 банана</p>
<p>11 бананов</p>
<p>31 банан</p>
Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/guide/sfc.html ================================================ Однофайловые компоненты | Vue I18n

# Однофайловые компоненты

# Базовое использование

В компоненте Vue или приложении Vue с использованием однофайловых компонентов, можно управлять сообщениями локализации с помощью пользовательского блока i18n.

Код компонента из примера использования с однофайловыми компонентами:

<i18n>
{
  "en": {
    "hello": "hello world!"
  },
  "ru": {
    "hello": "Привет мир!"
  }
}
</i18n>

<template>
  <div id="app">
    <label for="locale">Язык</label>
    <select v-model="locale">
      <option>en</option>
      <option>ru</option>
    </select>
    <p>Сообщение: {{ $t('hello') }}</p>
  </div>
</template>

<script>
export default {
  name: 'app',
  data () {
    this.$i18n.locale = 'ru';
    return { locale: 'ru' }
  },
  watch: {
    locale (val) {
      this.$i18n.locale = val
    }
  }
}
</script>

# Установка vue-i18n-loader

Требуется установить vue-loader и vue-i18n-loader чтобы использовать пользовательские блоки <i18n>. Скорее всего vue-loader уже используется в проекте, если уже работаете с однофайловыми компонентами, но необходимо дополнительно установить vue-i18n-loader:

npm i --save-dev @kazupon/vue-i18n-loader

# Webpack

Для Webpack требуется следующая конфигурация:

Для vue-loader v15 или более поздних версий:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        resourceQuery: /blockType=i18n/,
        type: 'javascript/auto',
        loader: '@kazupon/vue-i18n-loader'
      }
      // ...
    ]
  }
  // ...
}

Для vue-loader v14:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            // необходимо указать ключ `i18n` для загрузчика `vue-i18n-loader`
            // (https://github.com/kazupon/vue-i18n-loader)
            i18n: '@kazupon/vue-i18n-loader'
          }
        }
      }
      // ...
    ]
  }
  // ...
}

# Vue CLI 3.0

Vue CLI 3.0 скрывает конфигурацию Webpack, поэтому для добавления поддержки тегов <i18n> в однофайловых компонентах необходимо изменить существующую конфигурацию.

Для этого нужно создать файл vue.config.js в корне проекта и добавить в него следующее:

Для vue-loader v15 или более поздних версий:

module.exports = {
  chainWebpack: config => {
    config.module
      .rule('i18n')
      .resourceQuery(/blockType=i18n/)
      .type('javascript/auto')
      .use('i18n')
      .loader('@kazupon/vue-i18n-loader')
      .end()
  }
}

Для vue-loader v14:

const merge = require('deepmerge')

module.exports = {
  chainWebpack: config => {
    config.module
      .rule('vue')
      .use('vue-loader')
      .tap(options =>
        merge(options, {
          loaders: {
            i18n: '@kazupon/vue-i18n-loader'
          }
        })
      )
  }
}

Не забудьте установить deepmerge! (npm i deepmerge -D или yarn add deepmerge -D)

Подробнее о возможностях изменения существующей конфигурации Webpack можно изучить здесь.

# Laravel-Mix

Для Laravel-mix 4 с vue-loader v15 или более поздней версии:

// Расширяем Mix с помощью метода "i18n", который загрузит vue-i18n-loader
mix.extend( 'i18n', new class {
        webpackRules() {
            return [
                {
                    resourceQuery: /blockType=i18n/,
                    type:          'javascript/auto',
                    loader:        '@kazupon/vue-i18n-loader',
                },
            ];
        }
    }(),
);

// Убедитесь что вызвали .i18n() (для загрузки загрузчика) перед .js(..., ...)
mix.i18n()
   .js( 'resources/js/App.js', 'public/js/app.js' )
   ...

Для Laravel-mix 2 с vue-loader v14:

В Laravel-mix, начиная с версии V2.1, можно добавлять пользовательские правила с помощью mix.extend(). Laravel-mix уже имеет собственные правила для обработки .vue файлов. Чтобы добавить vue-i18n-loader, нужно добавить в webpack.mix.js следующее:

// Код ниже внедрит загрузчик i18n (@kazupon/vue-i18n-loader) в качестве загрузчика .vue файлов.
mix.extend( 'i18n', function( webpackConfig, ...args ) {
    webpackConfig.module.rules.forEach( ( module ) => {
        // Поиск компонента "vue-loader", который обрабатывает .vue файлы.
        if( module.loader !== 'vue-loader' ) {
            return;
        }

        // В этом модуле добавляем vue-i18n-loader для тега i18n.
        module.options.loaders.i18n = '@kazupon/vue-i18n-loader';
    } );
} );

// Убедитесь что вызвали .i18n() (для загрузки загрузчика) перед .js(..., ...)
mix.i18n()
   .js( 'resources/assets/js/App.js', 'public/js/app.js' )
   ...

# Загрузка YAML

Пользовательские блоки i18n можно указывать в формате JSON или YAML (используя функцию предварительного загрузчика vue-loader).

Пользовательский блок i18n в формате YAML:

<i18n>
  en:
    hello: "hello world!"
  ru:
    hello: "привет мир!"
</i18n>

Конфигурация Webpack:

Для vue-loader v15 или более поздних версий:

// Vue CLI 3.0
module.exports = {
  chainWebpack: config => {
    config.module
      .rule('i18n')
      .resourceQuery(/blockType=i18n/)
      .type('javascript/auto')
      .use('i18n')
      .loader('@kazupon/vue-i18n-loader')
      .end()
      .use('yaml')
      .loader('yaml-loader')
      .end()
  }
}

Для vue-loader v14:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          preLoaders: {
            i18n: 'yaml-loader'
          },
          loaders: {
            i18n: '@kazupon/vue-i18n-loader'
          }
        }
      }
      // ...
    ]
  }
  // ...
}

# Несколько пользовательских блоков

Можно использовать сообщения локализации из нескольких пользовательских блоков i18n.

<i18n src="./common/locales.json"></i18n>
<i18n>
  {
    "en": {
      "hello": "hello world!"
    },
    "ru": {
      "hello": "Привет мир!"
    }
  }
</i18n>

В примере выше, первый пользовательский блок загружает общие сообщения локализации с помощью атрибута src, второй пользовательский блок загружает сообщения локализации, которые определены только в этом однофайловом компоненте. Все они будут объединены в качестве сообщений локализации компонента.

Несколько пользовательских блоков полезны, когда использовать их в качестве модулей.

# Локальные стили

При использовании vue-i18n с локальными стилями (style scoped) необходимо помнить и использовать глубокий селектор для стилизации элемента внутри строки перевода. Например:

Когда перевод содержит только текст (работает без глубокого селектора)

<i18n>
  {
    "en": {
      "hello": "hello world!"
    },
    "ru": {
      "hello": "Привет мир!"
    }
  }
</i18n>

<template>
  <div class="parent">
    <p>Сообщение: {{ $t('hello') }}</p>
  </div>
</template>

<!-- Будет работать -->
<style>
  .parent p {
    color: #42b883;
  }
</style>

Когда перевод содержит HTML-элемент (необходимо использовать глубокий селектор)

<i18n>
  {
    "en": {
      "hello": "hello<span>world!</span>"
    },
    "ru": {
      "hello": "привет <span>мир!</span>"
    }
  }
</i18n>

<template>
  <div class="parent">
    <p v-html="$t('hello')"></p>
  </div>
</template>

<!-- НЕ БУДЕТ РАБОТАТЬ! -->
<style>
  .parent p {
    color: #42b883;
  }

  .parent p span {
    color: red;
  }
</style>

<!-- Будет работать -->
<style>
  .parent p {
    color: #42b883;
  }

  .parent p >>> span {
    color: red;
  }
</style>

# Пользовательские блоки в функциональном компоненте

Если в шаблоне однофайловых компонентов используется функциональный компонент и определены пользовательские блоки i18n, то обратите внимание что невозможно локализовать с помощью сообщений локализации.

Например, следующий код не может использовать сообщения локализации из блока i18n.

<i18n>
  {
    "en": {
      "hello": "hello world"
    },
    "ru": {
      "hello": "привет мир"
    }
  }
</i18n>

<template functional>
  <!-- Сообщение локализации 'hello' из родительского экземпляра -->
  <p>{{ parent.$t('hello') }}</p>
</template>
Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/guide/tooling.html ================================================ Инструментарий | Vue I18n

# Инструментарий

Для поддержки i18n приложений Vue некоторые инструменты предоставляются официально.

Также есть инструменты от сторонних разработчиков, которые интегрируются в Vue I18n.

# Официальный инструментарий

# Плагин для Vue CLI

vue-cli-plugin-i18n — официальный плагин для Vue CLI.

С помощью этого плагина можно настроить среду i18n для приложения Vue и поддерживать среду разработки i18n.

# Модуль для Nuxt

nuxt-i18n — соответствующий модуль для Nuxt.js.

# Загрузчик для Webpack

vue-i18n-loader — официальный загрузчик для webpack.

С помощью этого загрузчика можно использовать пользовательские блоки i18n в однофайловых компонентах.

Подробнее о пользовательских блоках i18n можно изучить в разделе Однофайловых компонентов

# Плагин для ESLint

eslint-plugin-vue-i18n — ESLint-плагин для Vue I18n.

Позволяет легко интегрировать функции проверки локализацией в ваше приложение Vue.js.

# Расширения

vue-i18n-extensions — предоставляет некоторые расширения дляVue I18n.

Эти расширения позволяет использовать в рендеринге на стороне сервера (SSR) и улучшить производительность i18n.

# Сторонние разработки

# BabelEdit

BabelEdit — редактор переводов для веб-приложений.

BabelEdit может переводить файлы json, а также умеет работать с пользовательскими блоками i18n однофайловых компонентов.

Подробнее про BabelEdit можно узнать на странице введения.

# i18n Ally

i18n Ally — расширение i18n для VSCode.

i18n Ally предоставляет потрясающий DX для разработки с использованием i18n.

Подробнее о расширении i18n Ally можно изучить в README.

Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/index.html ================================================ Vue I18n
hero

Vue I18n

Vue I18n — плагин для интернационализации во Vue.js

Введение →

Серебряные спонсоры

Become a Patreon

Простой

Позволяет легко добавить интернационализацию в приложение с помощью простого API

Функциональный

В дополнение к переводам, поддерживает плюрализацию, локализацию для чисел, дат ... и т.д.

Ориентированный на компоненты

Можно управлять сообщениями локализации в однофайловых компонентах

================================================ FILE: docs/ru/installation.html ================================================ Установка | Vue I18n

# Установка

# Примечание совместимости

  • Vue.js версии 2.0.0+

# Загрузка файла / CDN

https://unpkg.com/vue-i18n/dist/vue-i18n

Сервис unpkg.com предоставляет CDN-ссылки на основе NPM-пакетов. Ссылка выше будет всегда указывать на последнюю версию на NPM. Можно использовать конкретную версию или тег с помощью URL следующего вида https://unpkg.com/vue-i18n@8.17.3/dist/vue-i18n.js

При подключении vue-i18n после Vue плагин установит себя автоматически:

<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-i18n/dist/vue-i18n.js"></script>

# NPM

npm install vue-i18n

# Yarn

yarn add vue-i18n

При использовании системы модулей нужно явно устанавливать vue-i18n через Vue.use():

import Vue from 'vue'
import VueI18n from 'vue-i18n'

Vue.use(VueI18n)

Подобного не требуется делать при подключении через глобальный тег <script>.

# Vue CLI 3.x

vue add i18n

Предварительно требуется установить Vue CLI 3.x, его можно установить следующей командой:

npm install @vue/cli -g

# Dev-сборка

При необходимости использовать последнюю dev-сборку нужно склонировать репозиторий с GitHub и выполнить сборку vue-i18n самостоятельно.

git clone https://github.com/kazupon/vue-i18n.git node_modules/vue-i18n
cd node_modules/vue-i18n
npm install # или `yarn`
npm run build  # или `yarn run build`

# Отличия различных сборок

Внутри каталога dist / NPM-пакета можно обнаружить несколько различных сборок VueI18n. Вот следующие отличия между ними:

  • UMD: vue-i18n.js
  • CommonJS: vue-i18n.common.js
  • ES Module для систем сборки: vue-i18n.esm.js
  • ES Module для браузеров: vue-i18n.esm.browser.js

# Термины

  • UMD: UMD-сборки можно использовать непосредственно в браузере через тег <script>. Файл по умолчанию с Unpkg CDN https://unpkg.com/vue-i18n указывает на UMD-сборку (vue-i18n.js).

  • CommonJS: CommonJS сборки предназначены для использования со старыми системами сборки, такими как browserify или webpack 1. Файлом по умолчанию для этих систем сборки (pkg.main) будет сборка CommonJS (vue-i18n.common.js).

  • ES Module: VueI18n, начиная с версии 8.11, предоставляет две сборки ES-модулей (ESM):

    • ESM для систем сборки: предназначен для использования с современными системами сборки, такими как webpack 2 или Rollup. Формат ESM разработан для возможности статического анализа, чтобы системы сборки могли применять "tree-shaking" и удалять неиспользуемый код из финального приложения. Файлом по умолчанию для этих систем сборки (pkg.module) будет ES-сборка (vue-i18n.esm.js).
    • ESM для браузеров (только для 8.11+, vue-i18n.esm.browser.js): предназначен для импорта напрямую в современных браузерах через тег <script type="module">.
Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/introduction.html ================================================ Введение | Vue I18n

# Введение

Предупреждение

⚠️ Документация для Vue I18n v6.0 или более поздних версий. Если используете и ищете документацию для версии v5.x, обратитесь к разделу устаревшей версии.

Vue I18n — плагин для интернационализации во Vue.js. Он легко интегрирует дополнительные возможности по локализации приложения Vue.js.

Изучение лучше начинать с раздела Начало работы

# Спонсоры

# Серебряные

# Поддержите на Patreon

Ваша компания для создания потрясающих приложений использует vue-i18n или vue-cli-plugin-i18n? Присоединяйтесь к другим патронам или становитесь спонсором, чтобы увидеть свой логотип в документации! Поддержка на Patreon позволяет автору меньше работать и больше заниматься развитием свободным опенсорсом, таким как vue-i18n! Спасибо!

Become a Patreon

Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/legacy/index.html ================================================ Миграция с версии v5.x | Vue I18n

# Миграция с версии v5.x

# Глобальная конфигурация

# Заменён lang

Используйте у конструктора класса VueI18n опцию locale или свойство VueI18n#locale:

const i18n = new VueI18n({
  locale: 'en'
  // ...
})
const app = new Vue({ i18n }).$mount('#app')

// переключение локализации
i18n.locale = 'ru'
// или
app.$i18n.locale = 'ru'

# Заменён fallbackLang

Используйте у конструктора класса VueI18n опцию fallbackLocale или свойство VueI18n#fallbackLocale:

const i18n = new VueI18n({
  locale: 'ru',
  fallbackLocale: 'en'
  // ...
})
const app = new Vue({ i18n }).$mount('#app')

// изменение запасной локализации
i18n.fallbackLocale = 'zh'
// или
app.$i18n.fallbackLocale = 'zh'

# Заменён missingHandler

Используйте у конструктора класса VueI18n опцию missing или свойство VueI18n#missing:

const i18n = new VueI18n({
  // ...
  missing: (locale, key, vm, values) => {
    // обработка отсутствующих переводов
  }
  // ...
})
const app = new Vue({ i18n }).$mount('#app')

// изменение обработчика для отсутствующих переводов
i18n.missing = (locale, key, vm, values) => {
  // обработка отсутствующих переводов
}
// или
app.$i18n.missing = (locale, key, vm, values) => {
  // обработка отсутствующих переводов
}

# Заменён i18nFormatter

Используйте у конструктора класса VueI18n опцию formatter или свойство VueI18n#formatter:

class CustomFormatter {
  format(message, ...values) {
    // какая-то логика
    return 'something string'
  }
}

const i18n = new VueI18n({
  // ...
  formatter: new CustomFormatter()
  // ...
})
const app = new Vue({ i18n }).$mount('#app')

// изменение пользовательского форматтера
i18n.formatter = {
  format: (message, ...values) => {
    // какая-то логика
    return 'something string'
  }
}
// или
app.$i18n.formatter = {
  format: (message, ...values) => {
    // какая-то логика
    return 'something string'
  }
}

# Глобальные методы

# Заменён Vue.locale

Используйте у конструктора класса VueI18n опцию messages или методы VueI18n#GetLocaleMessage / VueI18n#setLocaleMessage:

const i18n = new VueI18n({
  // ...
  messages: {
    en: {
      hello: 'hello world'
      // ...
    },
    ru: {
      hello: 'привет мир'
      // ...
    }
  }
  // ...
})
const app = new Vue({ i18n }).$mount('#app')

// получение сообщений локализации
const en = i18n.getLocaleMessage('en')
en.greeting = 'hi!'
// установка сообщений локализации
i18n.setLocaleMessage('en', en)
// или
const ru = app.$i18n.getLocaleMessage('ru')
ru.greeting = 'Привет!'
app.$i18n.setLocaleMessage('ru', ru)

# Заменён Vue.t

Используйте метод VueI18n#t:

const i18n = new VueI18n({
  locale: 'en',
  messages: {
    en: {
      greeting: 'hi {name}'
    }
  }
  // ...
})

i18n.t('greeting', { name: 'kazupon' }) // -> hi kazupon

# Заменён Vue.tc

Используйте метод VueI18n#tc:

const i18n = new VueI18n({
  locale: 'en',
  messages: {
    en: {
      apple: 'no apples | one apple | {count} apples'
    }
  }
  // ...
})

const count = 10
i18n.tc('apple', count, { count }) // -> 10 apples

# Заменён Vue.te

Используйте метод VueI18n#te:

const i18n = new VueI18n({
  locale: 'en',
  messages: {
    en: {
      hello: 'hello world'
    }
  }
  // ...
})

i18n.te('hello') // -> true
i18n.te('hallo', 'ru') // -> false
i18n.te('hello') // -> true

# Опции конструктора

# Заменён locales

Используйте у конструктора класса VueI18n опцию messages или messages из i18n (для компонента):

const i18n = new VueI18n({
  locale: 'en',
  messages: {
    en: {
      greeting: 'hi {name}'
    }
  }
  // ...
})

// для компонента
const Component1 = {
  i18n: {
    messages: {
      en: {
        title: 'Title1'
      }
    }
  }
}

# Свойства экземпляра

# Заменён $lang

Используйте свойство VueI18n#locale:

const i18n = new VueI18n({
  locale: 'en'
  // ...
})
const app = new Vue({ i18n }).$mount('#app')

// переключение локализации
i18n.locale = 'ru'
// или
app.$i18n.locale = 'ru'

# Другие возможности

# Динамическая локализация удалена

При необходимости динамически устанавливать сообщения локализации потребуется добавить реализацию самостоятельно:

const i18n = new VueI18n({ locale: 'en' })
const app = new Vue({
  i18n,
  data: { loading: '' }
}).$mount('#app')

function loadLocaleMessage(locale, cb) {
  return fetch('/locales/' + locale, {
    method: 'get',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json'
    }
  })
    .then(res => {
      return res.json()
    })
    .then(json => {
      if (Object.keys(json).length === 0) {
        return Promise.reject(new Error('locale empty !!'))
      } else {
        return Promise.resolve(json)
      }
    })
    .then(message => {
      cb(null, message)
    })
    .catch(error => {
      cb(error)
    })
}

app.loading = 'loading ...'
loadLocaleMessage('en', (err, message) => {
  if (err) {
    app.loading = ''
    console.error(err)
    return
  }
  i18n.setLocaleMessage('en', message)
  app.loading = ''
})
Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/ru/legacy/v5.html ================================================ Документация для версии v5.x | Vue I18n
================================================ FILE: docs/ru/started.html ================================================ Начало работы | Vue I18n

# Начало работы

ПРИМЕЧАНИЕ

В примерах кода этого руководства используется ES2015.

# HTML

<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-i18n/dist/vue-i18n.js"></script>

<div id="app">
  <p>{{ $t("message.hello") }}</p>
</div>

# JavaScript

// При использовании модульной системы (например, через vue-cli)
// нужно импортировать Vue и VueI18n и вызвать Vue.use(VueI18n).
//
// import Vue from 'vue'
// import VueI18n from 'vue-i18n'
//
// Vue.use(VueI18n)

// Готовые переводы сообщений локализаций
const messages = {
  en: {
    message: {
      hello: 'hello world'
    }
  },
  ru: {
    message: {
      hello: 'Привет мир'
    }
  }
}

// Создание экземпляра VueI18n с настройками
const i18n = new VueI18n({
  locale: 'ru', // установка локализации по умолчанию
  messages // установка сообщений локализаций
})

// Создание экземпляра Vue с опцией `i18n`
new Vue({ i18n }).$mount('#app')

// Теперь можно запускать приложение!

Результат будет таким:

<div id="#app">
  <p>Привет мир</p>
</div>
Последнее обновление: 5/7/2020, 3:46:10 AM
================================================ FILE: docs/started.html ================================================ Getting started | Vue I18n

# Getting started

NOTE

We will be using ES2015 in the code samples in the guide.

# HTML

<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-i18n/dist/vue-i18n.js"></script>

<div id="app">
  <p>{{ $t("message.hello") }}</p>
</div>

# JavaScript

// If using a module system (e.g. via vue-cli), import Vue and VueI18n and then call Vue.use(VueI18n).
// import Vue from 'vue'
// import VueI18n from 'vue-i18n'
//
// Vue.use(VueI18n)

// Ready translated locale messages
const messages = {
  en: {
    message: {
      hello: 'hello world'
    }
  },
  ja: {
    message: {
      hello: 'こんにちは、世界'
    }
  }
}

// Create VueI18n instance with options
const i18n = new VueI18n({
  locale: 'ja', // set locale
  messages, // set locale messages
})


// Create a Vue instance with `i18n` option
new Vue({ i18n }).$mount('#app')

// Now the app has started!

Output the following:

<div id="#app">
  <p>こんにちは、世界</p>
</div>
Last Updated: 2/5/2019, 3:35:51 PM
================================================ FILE: docs/zh/api/index.html ================================================ API参考 | Vue I18n

# API参考

# 扩展 Vue

# Vue 构造函数选项

# i18n

  • 类型:I18nOptions

基于组件的本地化选项

  • 请参阅:VueI18n 类构造函数选项

# Vue 注入方法

# $t

  • 参数:

    • {Path} key:必填
    • {Locale} locale:可选
    • {Array | Object} values:可选
  • 返回值:TranslateResult

本地化语言环境信息 key,在本地化时组件的语言环境信息优先于全局语言环境信息。如果未指定组件的语言环境信息,就使用全局语言环境信息进行本地化。如果你指定了 locale 参数,则使用 locale 提供的语言环境进行本地化。如果你为列表/格式化的语言环境信息指定了 key,就必须同时指定 values。有关 values 的详细信息,请参阅格式化

提示

注意,你需要在生命周期方法中保证上下文是组件实例 (例如在 data 选项中,const $t = this.$t.bind(this))。

# $tc

  • 参数:

    • {Path} key:必填
    • {number} choice:可选,默认为 1
    • {Locale} locale:可选
    • {string | Array | Object} values:可选
  • 返回值:TranslateResult

以复数形式将语言环境信息 key 本地化。在本地化时组件的语言环境信息优先于全局语言环境信息。如果未指定组件的语言环境信息,就使用全局语言环境信息进行本地化。如果你指定了 locale 参数,则使用 locale 提供的语言环境进行本地化。如果将 values 指定为字符串,则该字符串会作为语言环境信息进行本地化。如果将 values 指定为 Array 或 Object,则格式必须为 $tvalues

提示

注意,你需要在生命周期方法中保证上下文是组件实例 (例如在 data 选项中,const $tc = this.$tc.bind(this))

# $te

  • 参数:

    • {Path} key:必填
    • {Locale} locale:可选
  • 返回值:boolean

检查 key 是否存在。在 Vue 实例中,如果未指定组件语言环境信息,则使用全局语言环境信息。如果指定了 locale,则使用 locale 的语言环境。

提示

注意,你需要在生命周期方法中保证上下文是组件实例 (例如在 data 选项中,const $te = this.$te.bind(this))。

# $d

🆕 7.0 新增

  • 参数:

    • {number | Date} value:必填
    • {Path | Object} key:可选
    • {Locale | Object} locale:可选
  • 返回值:DateTimeFormatResult

将日期时间 valuekey 的格式本地化。日期时间格式 key 需要注册到 VueI18n 类的 dateTimeFormats 选项,并依赖于 VueI18n 构造函数的 locale 选项。如果要指定 locale 参数,它将优先于 VueI18n 构造函数的 locale 选项。

如果 dateTimeFormats 选项中不存在日期时间格式的 key,则根据 VueI18n 构造函数的 fallbackLocale 选项回退。

提示

注意,你需要在生命周期方法中保证上下文是组件实例 (例如在 data 选项中,const $d = this.$d.bind(this))。

# $n

🆕 7.0 新增

  • 参数:

    • {number} value:必填
    • {Path | Object} key:可选
    • {Locale} locale:可选
  • 返回值:NumberFormatResult

将数字 valuekey 的格式本地化。数字格式 key 需要注册到 VueI18n 类的 numberFormats 选项,并依赖于 VueI18n 构造函数的 locale 选项。如果要指定 locale 参数,它将优先于 VueI18n 构造函数的 locale 选项。

如果 numberFormats 选项中不存在用数字格式 key,则根据 VueI18n 构造函数的 fallbackLocale 选项回退。

如果第二个 key 参数指定为对象,则它应具有以下属性:

  • key {Path}:可选,数字格式
  • locale {Locale}:可选,语言环境
  • style {string}:可选,数字格式选项
  • currency {string}:可选,数字格式选项
  • currencyDisplay {string}:可选,数字格式选项
  • useGrouping {string}:可选,数字格式选项
  • minimumIntegerDigits {string}:可选,数字格式选项
  • minimumFractionDigits {string}:可选,数字格式选项
  • maximumFractionDigits {string}:可选,数字格式选项
  • minimumSignificantDigits {string}:可选,数字格式选项
  • maximumSignificantDigits {string}:可选,数字格式选项
  • localeMatcher {string}:可选,数字格式选项
  • formatMatcher {string}:可选,数字格式选项

任何指定数字的格式选项将优先于 VueI18n 构造函数的 numberFormats

提示

注意,你需要在生命周期方法中保证上下文是组件实例 (例如在 data 选项中,const $n = this.$n.bind(this))。

# 注入属性

# $i18n

  • 类型:I18n

  • 只读

若已经指定了 VueI18n 实例,则将其返回。

如果在组件选项中指定了 i18n 选项,则可以在组件上获得 VueI18n 实例,否则,你将获得 VueI18n 的根实例。

# VueI18n

Vuei18n 类实现了 I18n flowtype 接口

# 静态属性

# 版本

  • 类型:string

vue-i18n 版本

# 可用性

🆕 7.0 新增

  • 类型:IntlAvailability

是否提供以下国际化功能:

  • {boolean} dateTimeFormat:环境敏感的时间格式

  • {boolean} numberFormat:环境敏感的数字格式

由于使用 ECMAScript Internationalization API (ECMA-402) 实现,上述国际化功能取决于浏览器环境

# 构造函数选项

你可以基于flowtype 定义 中的 I18nOptions 指定下列构造函数选项

# locale

  • 类型:Locale

  • 默认值:'en-US'

语言环境。

# fallbackLocale

  • 类型:Locale

  • 默认值:'en-US'

预设的语言环境。

# messages

  • 类型:LocaleMessages

  • 默认值:{}

本地化的语言环境信息。

# dateTimeFormats

🆕 7.0 新增

  • 类型:DateTimeFormats

  • 默认值:{}

本地化的日期时间格式。

# numberFormats

🆕 7.0 新增

  • 类型:NumberFormats

  • 默认值:{}

本地化的数字格式。

# availableLocales

🆕 8.9.0 新增

  • 类型:Locale[]

  • 默认值:[]

  • 示例:["en", "ja"]

以词法顺序排列的 messages 中的可用语言环境列表。

# formatter

  • 类型:Formatter

  • 默认值: Built in formatter

使用 Formatter 接口实现的格式化。

# missing

  • 类型:MissingHandler

  • 默认值:null

缺少本地化时的处理函数。该处理函数在被调用时会使用本地化目标语言环境,本地化路径关键字和 Vue 实例。

如果设置了该函数,则本地化信息未定义时不会产生警告。

# fallbackRoot

  • 类型:Boolean

  • 默认值:true

在组件本地化中,当本地化失败时是否回退到根级别 (全局) 本地化。

如果为 false,则会发出警告,并返回 key。

# sync

  • 类型:Boolean

  • 默认值:true

是否将根级别语言环境与组件本地化语言环境同步。

如果为 false,则无论根级别语言环境如何,都要为每个组件语言环境进行本地化。

# silentTranslationWarn

6.1 新增

  • 类型:Boolean

  • 默认值:false

是否取消本地化失败时输出的警告。

如果为 true,则禁止本地化失败警告。

# silentFallbackWarn

🆕 8.8 新增

  • 类型:Boolean
  • 默认值:false

是否在回退到 fallbackLocaleroot 时取消警告。

如果为 true,则仅在根本没有可用的转换时生成警告,而不是在回退时。

# preserveDirectiveContent

8.7 新增

  • 类型:Boolean

  • 默认值:false

在指令解除绑定后,v-t 指令的元素是否应该保留 textContent

# Properties

# locale

  • 类型:Locale

  • 可读/可写

语言环境。

# fallbackLocale

  • 类型:Locale

  • 可读/可写

预设的语言环境。

# messages

  • 类型:LocaleMessages

  • 只读

本地化的语言环境信息。

# dateTimeFormats

🆕 7.0 新增

  • 类型:DateTimeFormats

  • 只读

本地化的日期时间格式。

# numberFormats

🆕 7.0 新增

  • 类型:NumberFormats

  • 只读

本地化的数字格式。

# missing

  • 类型:MissingHandler

  • 可读/可写

缺少本地化时的处理函数。

# formatter

  • 类型:Formatter

  • 可读/可写

使用 Formatter 接口实现的格式化。

# silentTranslationWarn

6.1 新增

  • 类型:boolean

  • 可读/可写

是否取消本地化失败时输出的警告。

# preserveDirectiveContent

8.7 新增

  • 类型:boolean

  • 可读/可写

在指令解除绑定后,v-t 指令的元素是否应该保留 textContent

# 方法

# getChoiceIndex

  • 参数:

    • {number} choice
    • {number} choicesLength
  • 返回值:finalChoice {number}

根据当前的数字和一组给定的选项,获取其复数索引,可以通过原型变更覆盖:

VueI18n.prototype.getChoiceIndex = /* 自定义实现 */

# getLocaleMessage( locale )

  • 参数:

    • {Locale} locale
  • 返回值:LocaleMessageObject

获取语言环境的 locale 信息。

# setLocaleMessage( locale, message )

  • 参数:

    • {Locale} locale
    • {LocaleMessageObject} message

设置语言环境的 locale 信息。

# mergeLocaleMessage( locale, message )

6.1 新增

  • 参数:

    • {Locale} locale
    • {LocaleMessageObject} message

将语言环境信息 locale 合并到已注册的语言环境信息中。

# t( key, [locale], [values] )

  • 参数:

    • {Path} key:必填
    • {Locale} locale:可选
    • {Array | Object} values:可选
  • 返回值:TranslateResult

这与 $t 方法返回的 Function 相同。更多细节见$t

# tc( key, [choice], [values] )

  • 参数:

    • {Path} key:必填
    • {number} choice:可选,默认为 1
    • {string | Array | Object} values:可选
  • 返回值:TranslateResult

这与 $tc 方法返回的 Function 相同。更多细节见$tc

# te( key, [locale] )

  • 参数:

    • {string} key:必填
    • {Locale} locale:可选
  • 返回值:boolean

检查全局语言环境信息中是否存在键名路径。如果指定了 locale,请检查语言环境信息 locale

# getDateTimeFormat ( locale )

🆕 7.0 新增

  • 参数:

    • {Locale} locale
  • 返回值:DateTimeFormat

获取语言环境的日期时间格式。

# setDateTimeFormat ( locale, format )

🆕 7.0 新增

  • 参数:

    • {Locale} locale
    • {DateTimeFormat} format

设置语言环境的日期时间格式。

# mergeDateTimeFormat ( locale, format )

🆕 7.0 新增

  • 参数:

    • {Locale} locale
    • {DateTimeFormat} format

将已注册的日期时间格式与语言环境的日期时间格式合并。

# d( value, [key], [locale] )

🆕 7.0 新增

  • 参数:

    • {number | Date} value:必填
    • {Path | Object} key:可选
    • {Locale | Object} locale:可选
  • 返回值:DateTimeFormatResult

这与 Vue 实例方法的 $d 方法相同。更多细节见$d

# getNumberFormat ( locale )

🆕 7.0 新增

  • 参数:

    • {Locale} locale
  • 返回值:NumberFormat

获取语言环境的数字格式。

# setNumberFormat ( locale, format )

🆕 7.0 新增

  • 参数:

    • {Locale} locale
    • {NumberFormat} format

设置语言环境的数字格式。

# mergeNumberFormat ( locale, format )

🆕 7.0 新增

  • 参数:

    • {Locale} locale
    • {NumberFormat} format

将已注册的数字格式与语言环境的数字格式合并。

# n( value, [key], [locale] )

🆕 7.0 新增

  • 参数:

    • {number} value:必填
    • {Path | Object} key:可选
    • {Locale} locale:可选
  • 返回值:NumberFormatResult

这与 Vue 实例方法的 $n 方法相同。更多细节见$n

# 指令

🆕 7.3 新增

# v-t

  • 预期:string | Object

  • 修饰符:

    • .preserve:(8.7.0 新增) 当指令解除绑定时,保留元素 textContent
  • 详细:

更新使用语言环境信息进行本地化的元素 textContent。你可以使用字符串语法或对象语法。字符串语法可以指定为语言环境信息的关键字路径。如果可以使用对象语法,则需要将以下参数指定为对象键:

* `path`:必填,语言环境信息的关键字
* `locale`:可选,语言环境
* `args`:可选,用于列表或命名格式

注意

v-t 指令解除绑定时,默认情况下将清除元素 textContent。在过渡动画内部使用的时候,可能出现不合预期的情况。为了在指令解除绑定之后保留 textContent 数据,可使用 .preserve 修饰符或全局的 preserveDirectiveContent 选项

  • 示例:
<!-- 字符串语法:字面量 -->
<p v-t="'foo.bar'"></p>

<!-- 字符串语法:通过数据或计算属性绑定 -->
<p v-t="msg"></p>

<!-- 对象语法: 字面量 -->
<p v-t="{ path: 'hi', locale: 'ja', args: { name: 'kazupon' } }"></p>

<!-- 对象语法: 通过数据或计算属性绑定 -->
<p v-t="{ path: greeting, args: { name: fullName } }"></p>

<!-- `preserve` 修饰符 -->
<p v-t.preserve="'foo.bar'"></p>

# 组件

# i18n 函数式组件

🆕 7.0 新增

# 参数:

  • path {Path}:必填,关于语言环境信息的键名路径
  • locale {Locale}:可选,语言环境
  • tag {string}:可选,默认值 span
  • places {Array | Object}:可选 (7.2 新增)

# 用法:

<div id="app">
  <!-- ... -->
  <i18n path="term" tag="label" for="tos">
    <a :href="url" target="_blank">{{ $t('tos') }}</a>
  </i18n>
  <!-- ... -->
</div>
const messages = {
  en: {
    tos: 'Term of Service',
    term: 'I accept xxx {0}.'
  },
  ja: {
    tos: '利用規約',
    term: '私は xxx の{0}に同意します。'
  }
}

const i18n = new VueI18n({
  locale: 'en',
  messages
})
new Vue({
  i18n,
  data: {
    url: '/term'
  }
}).$mount('#app')

# 请参阅:

组件插值

# 特殊属性

# 地区

🆕 7.2 新增

# 预期:{number | string}

用于组件插槽,指示格式列表的索引值或具名格式的关键字。

有关详细用法,请参阅下面链接的指南部分。

# 请参阅:

组件插值

最近一次更新: 5/11/2020, 7:03:20 PM
================================================ FILE: docs/zh/guide/component.html ================================================ 基于组件的本地化 | Vue I18n

# 基于组件的本地化

通常语言环境信息 (例如:localemessages 等) 会被设置为 VueI18n 实例的构造函数选项,并且该实例会被作为 i18n 选项设置在 Vue 的根实例上。

因此你可以全局地在 Vue 的根实例以及任何被组合的组件中使用 $t 或者 $tc 进行翻译。当然面向 Vue 组件的设计,你也可以更方便的分别控制每个组件的语言环境信息。

基于组件的本地化示例:

// 为 Vue 的根实例设置语言环境信息
const i18n = new VueI18n({
  locale: 'ja',
  messages: {
    en: {
      message: {
        hello: 'hello world',
        greeting: 'good morning'
      }
    },
    ja: {
      message: {
        hello: 'こんにちは、世界',
        greeting: 'おはようございます'
      }
    }
  }
})

// 定义组件
const Component1 = {
  template: `
    <div class="container">
     <p>Component1 locale messages: {{ $t("message.hello") }}</p>
     <p>Fallback global locale messages: {{ $t("message.greeting") }}</p>
   </div>`,
  i18n: { // `i18n` 选项,为组件设置语言环境信息
    messages: {
      en: { message: { hello: 'hello component1' } },
      ja: { message: { hello: 'こんにちは、component1' } }
    }
  }
}

new Vue({
  i18n,
  components: {
    Component1
  }
}).$mount('#app')

模板:

<div id="app">
  <p>{{ $t("message.hello") }}</p>
  <component1></component1>
</div>

输出如下:

<div id="app">
  <p>こんにちは、世界</p>
  <div class="container">
    <p>Component1 locale messages: こんにちは、component1</p>
    <p>Fallback global locale messages: おはようございます</p>
  </div>
</div>

在上面的例子中,如果组件没有语言环境信息,它将回退到全局定义的本地化信息。组件使用根实例中设置的语言 (在上面的例子中:locale: 'ja')。

注意,在默认情况下,回退到根语言环境会在控制台中生成两个警告:

[vue-i18n] Value of key 'message.greeting' is not a string!
[vue-i18n] Fall back to translate the keypath 'message.greeting' with root locale.

为避免以上警告 (同时保留那些完全没有翻译给定关键字的警告) 需初始化 VueI18n 实例时设置 silentFallbackWarn:true

如果你希望在组件语言环境中进行本地化,可以在 i18n 选项中用 sync: falselocale

# 函数式组件的翻译

使用函数式组件时,所有数据 (包括 prop、子内容、插槽、父级内容等) 都通过包含属性的 context 传递,并且它无法识别 this 的范围,因此在函数式组件上使用 vue-i18n 时,你必须将 $t 称为 parent.$t,请查看以下示例:

...
<div>
  <a
    href="#"
    target="_blank"
    rel="noopener noreferrer">
    <img src="" :alt="parent.$t('message.hello')">
  </a>
</div>
...
最近一次更新: 3/27/2019, 11:07:32 AM
================================================ FILE: docs/zh/guide/datetime.html ================================================ 日期时间本地化 | Vue I18n

# 日期时间本地化

支持版本

🆕 7.0 新增

你可以使用你定义的格式来本地化日期时间。

日期时间格式如下:

const dateTimeFormats = {
  'en-US': {
    short: {
      year: 'numeric', month: 'short', day: 'numeric'
    },
    long: {
      year: 'numeric', month: 'short', day: 'numeric',
      weekday: 'short', hour: 'numeric', minute: 'numeric'
    }
  },
  'ja-JP': {
    short: {
      year: 'numeric', month: 'short', day: 'numeric'
    },
    long: {
      year: 'numeric', month: 'short', day: 'numeric',
      weekday: 'short', hour: 'numeric', minute: 'numeric', hour12: true
    }
  }
}

如上,你可以定义具名的 (例如:shortlong 等) 日期时间格式,并需要使用 ECMA-402 Intl.DateTimeFormat 的选项

之后就像语言环境信息一样,你需要指定 VueI18n 构造函数的 dateTimeFormats 选项:

const i18n = new VueI18n({
  dateTimeFormats
})

new Vue({
  i18n
}).$mount('#app')

模板如下:

<div id="app">
  <p>{{ $d(new Date(), 'short') }}</p>
  <p>{{ $d(new Date(), 'long', 'ja-JP') }}</p>
</div>

输出如下:

<div id="app">
  <p>Apr 19, 2017</p>
  <p>2017年4月19日(水) 午前2:19</p>
</div>
最近一次更新: 3/27/2019, 11:07:32 AM
================================================ FILE: docs/zh/guide/directive.html ================================================ 自定义指令本地化 | Vue I18n

# 自定义指令本地化

支持的版本

🆕 7.3 新增

你不仅可以使用 $t 方法进行翻译,还可以使用 v-t 自定义指令。

# 字符串语法

你可以使用字符串语法传递语言环境信息的键名路径。

JavaScript:

new Vue({
  i18n: new VueI18n({
    locale: 'en',
    messages: {
      en: { hello: 'hi there!' },
      ja: { hello: 'こんにちは!' }
    }
  }),
  data: { path: 'hello' }
}).$mount('#string-syntax')

模板:

<div id="string-syntax">
  <!-- 字符串 -->
  <p v-t="'hello'"></p>
  <!-- 通过数据进行键名路径绑定 -->
  <p v-t="path"></p>
</div>

输出:

<div id="string-syntax">
  <p>hi there!</p>
  <p>hi there!</p>
</div>

# 对象语法

你可以使用对象语法。

Javascript:

new Vue({
  i18n: new VueI18n({
    locale: 'en',
    messages: {
      en: { hello: 'hi {name}!' },
      ja: { hello: 'こんにちは、{name}!' }
    }
  }),
  computed: {
    nickName () { return 'kazupon' }
  },
  data: { path: 'hello' }
}).$mount('#object-syntax')

模板:

<div id="object-syntax">
  <!-- 文字 -->
  <p v-t="{ path: 'hello', locale: 'ja', args: { name: 'kazupon' } }"></p>
  <!-- 通过 `data` 绑定数据 -->
  <p v-t="{ path: path, args: { name: nickName } }"></p>
</div>

输出:

<div id="object-syntax">
  <p>こんにちは、kazupon!</p>
  <p>hi kazupon!</p>
</div>

# 使用翻译

支持版本

🆕 8.7 新增

v-t 指令应用于 <transition> 组件内的元素时,你可能会注意到过渡动画之后的翻译过的信息会消失。这与 <transition> 组件实现的方式有关——在过渡开始之前<transition> 组件内消失元素中的所有指令都将被销毁。此行为可能导致内容在短过渡时闪烁,但在长过渡时最明显。

为了确保在转换期间指令内容不会被触及,只需将.preserve 修饰符添加到 v-t 指令定义中。

Javascript:

new Vue({
  i18n: new VueI18n({
    locale: 'en',
    messages: {
      en: { preserve: 'with preserve' },
    }
  }),
  data: { toggle: true }
}).$mount('#in-transitions')

模板:

<div id="in-transitions">
  <transition name="fade">
    <span v-if="toggle" v-t.preserve="'preserve'"></span>
  </transition>
  <button @click="toggle = !toggle">Toggle</button>
</div>

也可以在 VueI18n 实例本身设置全局设置,这将对没有修饰符的所有 v-t 指令产生影响。

Javascript:

new Vue({
  i18n: new VueI18n({
    locale: 'en',
    messages: {
      en: { preserve: 'with preserve' },
    },
    preserveDirectiveContent: true
  }),
  data: { toggle: true }
}).$mount('#in-transitions')

模板:

<div id="in-transitions">
  <transition name="fade">
    <span v-if="toggle" v-t="'preserve'"></span>
  </transition>
  <button @click="toggle = !toggle">Toggle</button>
</div>

关于上面的例子,请参阅示例

# $t vs v-t

# $t

$t 是扩展的 Vue 实例方法, 它有以下优点和缺点:

# 优点

你可以灵活地在模板以及 Vue 实例的计算属性和方法中使用 mustash 语法 {{}}

# 缺点

$t每次重新渲染时都会被执行,因此它确实有翻译成本。

# v-t

v-t 是一个自定义指令,它有以下优点和缺点:

# 优点

v-t$t 方法具有更好的性能,因为在一次翻译时自定义指令会进行缓存。此外可以使用由 vue-i18n-extensions 提供的 Vue 编译器模块进行预翻译。

因此,可以进行更多性能优化

# 缺点

v-t 不能像 $t 一样灵活使用,它更复杂。带有 v-t 的翻译内容会被插入到元素的 textContent 中。此外,当你使用服务器渲染时,你需要设置自定义指令createRenderer 函数的 directives 选项。

最近一次更新: 3/27/2019, 11:07:32 AM
================================================ FILE: docs/zh/guide/fallback.html ================================================ 回退本地化 | Vue I18n

# 回退本地化

以下语言环境信息的 ja 语言环境中不存在 message 键:

const messages = {
  en: {
    message: 'hello world'
  },
  ja: {
  }
}

当为 VueI18n 构造函数选项指定 fallbackLocale 选项时,message 键使用 en 语言环境进行本地化:

const i18n = new VueI18n({
  locale: 'ja',
  fallbackLocale: 'en',
  messages
})

模板如下:

<p>{{ $t('message') }}</p>

输出如下:

<p>hello world</p>

注意,默认情况下回退到 fallbackLocale 会产生两个控制台警告:

[vue-i18n] Value of key 'message' is not a string!
[vue-i18n] Fall back to translate the keypath 'message' with 'en' locale.

为了避免这些警告 (同时保留那些完全没有翻译给定关键字的警告),需初始化 VueI18n 实例时设置 silentFallbackWarn:true

# 回退插值

由于翻译的键值是字符串,因此也可以作为翻译的值:

const messages = {
  ja: {
    'Hello world': 'こんにちは、世界'
  }
}

这是一种很自然的书写方式,如果在message中找不到相应的键值将回退到原本的语言:

注意: fallbackRoot的优先级高于formatFallbackMessages

<p>{{ $t('Hello world') }}</p>

为了实现此功能,可以通过设置formatFallbackMessagestrue

const messages = {
  ru: {
    'Hello {name}': 'Здравствуйте {name}'
  }
}

const i18n = new VueI18n({
  locale: 'ru',
  fallbackLocale: 'en',
  formatFallbackMessages: true,
  messages
})

当模板template如下时:

<p>{{ $t('Hello {name}', { name: 'John' }}) }}</p>
<p>{{ $t('The weather today is {condition}!', { condition: 'sunny' }) }}</p>

将会输出:

<p>Здравствуйте John</p>
<p>The weather today is sunny!</p>
最近一次更新: 2/25/2020, 4:17:34 PM
================================================ FILE: docs/zh/guide/formatting.html ================================================ 格式化 | Vue I18n

# 格式化

# 具名格式

语言环境信息如下:

const messages = {
  en: {
    message: {
      hello: '{msg} world'
    }
  }
}

模板如下:

<p>{{ $t('message.hello', { msg: 'hello' }) }}</p>

输出如下:

<p>hello world</p>

# 列表格式

语言环境信息如下:

const messages = {
  en: {
    message: {
      hello: '{0} world'
    }
  }
}

模板如下:

<p>{{ $t('message.hello', ['hello']) }}</p>

输出如下:

<p>hello world</p>

列表格式也接受类似数组的对象:

<p>{{ $t('message.hello', {'0': 'hello'}) }}</p>

输出如下:

<p>hello world</p>

# HTML 格式化

提示

⚠️ 在你的网站上动态插入任意 HTML 可能非常危险,因为它很容易导致 XSS 攻击。仅对可信内容使用 HTML 插值,而不对用户提供的内容使用。

我们建议使用组件插值 功能。

在某些情况下,你可能希望将翻译呈现为 HTML 信息而不是静态字符串。

const messages = {
  en: {
    message: {
      hello: 'hello <br> world'
    }
  }
}

模板如下:

<p v-html="$t('message.hello')"></p>

输出如下 (取代预先格式化的信息)

<p>hello
<!--<br> 存在,但呈现为 html 而不是字符串-->
world</p>

# 支持 ruby on rails 的 i18n 格式

语言环境信息如下:

const messages = {
  en: {
    message: {
      hello: '%{msg} world'
    }
  }
}

模板如下:

<p>{{ $t('message.hello', { msg: 'hello' }) }}</p>

输出如下:

<p>hello world</p>

# 自定义格式

有时,你可能需要使用自定义格式进行翻译 (例如:ICU 信息语法)。

你可以使用实现格式化接口 的自定义格式化函数来实现。

以下使用 ES2015 class 语法的自定义格式化函数:

// 实现自定义格式
class CustomFormatter {
     constructor (options) {
       // ...
     }

     //
     // 插值
     //
     // @param {string} 信息
     //   列表或具名格式的字符串。
     //   例如:
     //   - 具名格式:'Hi {name}'
     //   - 列表格式:'Hi {0}'
     //
     // @param {Object | Array} 值
     //   `message` 插值的值
     //   使用 `$t`, `$tc` 和 `i18n` 函数式组件传递值。
     //   e.g.
     //   - $t('hello', { name: 'kazupon' }) -> 传递值:Object `{ name: 'kazupon' }`
     //   - $t('hello', ['kazupon']) -> 传递值:Array `['kazupon']`
     //   - `i18n` 函数式组件 (组件插值)
     //     <i18n path="hello">
     //       <p>kazupon</p>
     //       <p>how are you?</p>
     //     </i18n>
     //     -> 传递值:Array (included VNode):
     //        `[VNode{ tag: 'p', text: 'kazupon', ...}, VNode{ tag: 'p', text: 'how are you?', ...}]`
     //
     // @return {Array<any>}
     //   插值,你需要返回以下内容:
     //   - 当使用 `$t` 或 `$tc` 数组中应该是字符串。
     //   - 当使用 `i18n` 函数式组件时 数组中应包含 VNode 对象。
     //
     interpolate (message, values) {
       // 在这里实现插值逻辑
       // ...

       // 返回插值数组
       return ['resolved message string']
     }
}

// 注册 `formatter` 选项
const i18n = new VueI18n({
  locale: 'en-US',
  formatter: new CustomFormatter(/* 这里是构造函数选项 */),
  messages: {
    'en-US': {
      // ...
    },
    // ...
  }
})

// 启动!
new Vue({ i18n }).$mount('#app')

你可以查看自定义格式化函数的官方示例

最近一次更新: 7/22/2019, 1:03:00 PM
================================================ FILE: docs/zh/guide/hot-reload.html ================================================ 热重载 | Vue I18n

# 热重载

你可以监视本地化文件中的更改,并将更改热重载到应用程序中。

// 语言环境信息
const messages = {
  // ...
}

// VueI18n 实例
const i18n = new VueI18n({
  locale: 'en',
  messages
})

// 运行程序
const app = new Vue({
  i18n,
  // ...
}).$mount('#app')

// 热更新
if (module.hot) {
  module.hot.accept(['./en', './ja'], function () {
    i18n.setLocaleMessage('en', require('./en').default)
    i18n.setLocaleMessage('ja', require('./ja').default)
    // 同样可以通过 $i18n 属性进行热更新
    // app.$i18n.setLocaleMessage('en', require('./en').default)
    // app.$i18n.setLocaleMessage('ja', require('./ja').default)
  })
}
最近一次更新: 5/11/2020, 7:03:20 PM
================================================ FILE: docs/zh/guide/interpolation.html ================================================ 组件插值 | Vue I18n

# 组件插值

# 基本用法

支持版本

🆕 7.0 新增

有时,我们需要使用包含 HTML 标签或组件的语言环境信息进行本地化。例如:

<p>I accept xxx <a href="/term">Terms of Service Agreement</a></p>

在上面的信息中,如果你使用 $t,可能你会尝试编写以下语言环境信息:

const messages = {
  en: {
    term1: 'I Accept xxx\'s',
    term2: 'Terms of Service Agreement'
  }
}

你可能会尝试在以下模板中实现:

<p>{{ $t('term1') }}<a href="/term">{{ $t('term2') }}</a></p>

输出:

<p>I accept xxx <a href="/term">Terms of Service Agreement</a></p>

这是非常麻烦的,如果在语言环境信息中配置 <a> 标签,则可能由于使用了 v-html="$t('term')" 进行本地化而存在被 XSS 攻击的可能性。

你可以使用 i18n 函数式组件来避免它。例如:

<div id="app">
  <!-- ... -->
  <i18n path="term" tag="label" for="tos">
    <a :href="url" target="_blank">{{ $t('tos') }}</a>
  </i18n>
  <!-- ... -->
</div>
const messages = {
  en: {
    tos: 'Term of Service',
    term: 'I accept xxx {0}.'
  },
  ja: {
    tos: '利用規約',
    term: '私は xxx の{0}に同意します。'
  }
}

const i18n = new VueI18n({
  locale: 'en',
  messages
})
new Vue({
  i18n,
  data: {
    url: '/term'
  }
}).$mount('#app')

输出如下:

<div id="app">
  <!-- ... -->
  <label for="tos">
    I accept xxx <a href="/term" target="_blank">Term of Service</a>.
  </label>
  <!-- ... -->
</div>

关于上面的例子,见示例

i18n 函数式组件的子元素用 path 属性的语言环境信息进行插值。在上面的例子中,

<a :href="url" target="_blank">{{ $t('tos') }}</a>

被插入了语言环境信息 term

在上面的示例中,组件插值遵循列表格式i18n 函数式组件的子项按其出现顺序进行插值。

# 高级用法

支持版本

🆕 7.2 新增

提示

⚠️ 在 i18n 组件中,仅包含空格的文本内容将被省略。

place 特性的帮助下支持具名格式。例如:

<div id="app">
  <!-- ... -->
  <i18n path="info" tag="p">
    <span place="limit">{{ changeLimit }}</span>
    <a place="action" :href="changeUrl">{{ $t('change') }}</a>
  </i18n>
  <!-- ... -->
</div>
const messages = {
  en: {
    info: 'You can {action} until {limit} minutes from departure.',
    change: 'change your flight',
    refund: 'refund the ticket'
  }
}

const i18n = new VueI18n({
  locale: 'en',
  messages
})
new Vue({
  i18n,
  data: {
    changeUrl: '/change',
    refundUrl: '/refund',
    changeLimit: 15,
    refundLimit: 30
  }
}).$mount('#app')

输出:

<div id="app">
  <!-- ... -->
  <p>
    You can <a href="/change">change your flight</a> until <span>15</span> minutes from departure.
  </p>
  <!-- ... -->
</div>

提示

⚠️ i18n 组件的所有子项都必须设置 place 属性。否则它将回退到列表格式。

如果你仍想在命名格式中插入文本内容,可以在 i18n 组件上定义 places 属性。例如:

<div id="app">
  <!-- ... -->
  <i18n path="info" tag="p" :places="{ limit: refundLimit }">
    <a place="action" :href="refundUrl">{{ $t('refund') }}</a>
  </i18n>
  <!-- ... -->
</div>

输出:

<div id="app">
  <!-- ... -->
  <p>
    You can <a href="/refund">refund your ticket</a> until 30 minutes from departure.
  </p>
  <!-- ... -->
</div>
最近一次更新: 3/27/2019, 11:07:32 AM
================================================ FILE: docs/zh/guide/lazy-loading.html ================================================ 延迟加载翻译 | Vue I18n

# 延迟加载翻译

一次加载所有翻译文件是过度和不必要的。

使用 Webpack 时,延迟加载或异步加载转换文件非常简单。

让我们假设我们有一个类似于下面的项目目录

our-cool-project
-dist
-src
--routes
--store
--setup
---i18n-setup.js
--lang
---en.js
---it.js

lang 文件夹是我们所有翻译文件所在的位置。setup 文件夹是我们的任意设置的文件,如 i18n-setup,全局组件 inits,插件 inits 和其他位置。

//i18n-setup.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import messages from '@/lang/en'
import axios from 'axios'

Vue.use(VueI18n)

export const i18n = new VueI18n({
  locale: 'en', // 设置语言环境
  fallbackLocale: 'en',
  messages // 设置语言环境信息
})

const loadedLanguages = ['en'] // 我们的预装默认语言

function setI18nLanguage (lang) {
  i18n.locale = lang
  axios.defaults.headers.common['Accept-Language'] = lang
  document.querySelector('html').setAttribute('lang', lang)
  return lang
}

export function loadLanguageAsync (lang) {
  if (i18n.locale !== lang) {
    if (!loadedLanguages.includes(lang)) {
      return import(/* webpackChunkName: "lang-[request]" */ `@/lang/${lang}`).then(msgs => {
        i18n.setLocaleMessage(lang, msgs.default)
        loadedLanguages.push(lang)
        return setI18nLanguage(lang)
      })
    }
    return Promise.resolve(setI18nLanguage(lang))
  }
  return Promise.resolve(lang)
}

简而言之,我们正在创建一个新的 VueI18n 实例。然后我们创建一个 loadedLanguages 数组,它将跟踪我们加载的语言。接下来是 setI18nLanguage 函数,它将实际更改 vueI18n 实例、axios 以及其它需要本地化的地方。

loadLanguageAsync 是实际用于更改语言的函数。加载新文件是通过import功能完成的,import 功能由 Webpack 慷慨提供,它允许我们动态加载文件,并且因为它使用 promise,我们可以轻松地等待加载完成。

你可以在 Webpack 文档 中了解有关导入功能的更多信息。

使用 loadLanguageAsync 函数很简单。一个常见的用例是在 vue-router beforeEach 钩子里面。

router.beforeEach((to, from, next) => {
  const lang = to.params.lang
  loadLanguageAsync(lang).then(() => next())
})

我们可以通过检查 lang 实际上是否支持来改进这一点,调用 reject 这样我们就可以在 beforeEach 捕获路由转换。

最近一次更新: 7/2/2019, 8:38:50 AM
================================================ FILE: docs/zh/guide/locale.html ================================================ 语言环境变更 | Vue I18n

# 语言环境变更

通常,使用 Vue 根实例作为起点,使用 VueI18n 类的 locale 属性作为参考来本地化所有子组件。

有时你可能希望动态更改语言环境。在这种情况下,你可以更改 VueI18n 实例的 locale 属性的值。

const i18n = new VueI18n({
  locale: 'ja', // 设置语言环境
  ...
})

// 创建 Vue 根实例
new Vue({
  i18n,
  ...
}).$mount('#app')

// 更改为其它的 locale
i18n.locale = 'en'

每个组件都包含一个引用为 $i18n 属性的 VueI18n 实例,该实例也可用于更改语言环境。

示例:

<template>
  <div class="locale-changer">
    <select v-model="$i18n.locale">
      <option v-for="(lang, i) in langs" :key="`Lang${i}`" :value="lang">{{ lang }}</option>
    </select>
  </div>
</template>

<script>
export default {
  name: 'locale-changer',
  data () {
    return { langs: ['ja', 'en'] }
  }
}
</script>

警告

⚠️ 对于使用了 sync: false 的组件,语言环境的更改将被忽略。

最近一次更新: 3/27/2019, 11:07:32 AM
================================================ FILE: docs/zh/guide/messages.html ================================================ 语言环境信息的语法 | Vue I18n

# 语言环境信息的语法

# 结构

语言环境信息的语法如下:

// 作为 Flowtype 定义,语言环境信息的语法类似于 BNF 注释
type LocaleMessages = { [key: Locale]: LocaleMessageObject };
type LocaleMessageObject = { [key: Path]: LocaleMessage };
type LocaleMessageArray = LocaleMessage[];
type LocaleMessage = string | LocaleMessageObject | LocaleMessageArray;
type Locale = string;
type Path = string;

基于以上语法,你可以配置以下结构的 Locale 信息:

{
  "en": {  // 'en' Locale
    "key1": "this is message1", // 基本的
    "nested": { // 嵌套
      "message1": "this is nested message1"
    },
    "errors": [ // 数组
      "this is 0 error code message",
      {  // 数组嵌套对象
        "internal1": "this is internal 1 error message"
      },
      [  // 数组嵌套数组
        "this is nested array error 1"
      ]
    ]
  },
  "ja": { // 'ja' Locale
    // ...
  }
}

在上面的语言环境信息的结构中,你可以使用以下键名路径进行翻译。

<div id="app">
  <!-- 基本的 -->
  <p>{{ $t('key1') }}</p>
  <!-- 嵌套 -->
  <p>{{ $t('nested.message1') }}</p>
  <!-- 数组 -->
  <p>{{ $t('errors[0]') }}</p>
  <!-- 数组嵌套对象 -->
  <p>{{ $t('errors[1].internal1') }}</p>
  <!-- 数组嵌套数组 -->
  <p>{{ $t('errors[2][0]') }}</p>
</div>

输出以下内容:

<div id="app">
  <!-- 基本的 -->
  <p>this is message1</p>
  <!-- 嵌套 -->
  <p>this is nested message1</p>
  <!-- 数组 -->
  <p>this is 0 error code message</p>
  <!-- 数组嵌套对象 -->
  <p>this is internal 1 error message</p>
  <!-- 数组嵌套数组 -->
  <p>this is nested array error 1</p>
</div>

# Linked locale messages

如果有一个翻译关键字总是与另一个具有相同的具体文本,你可以链接到它。要链接到另一个翻译关键字,你所要做的就是在其内容前加上一个 @: 符号后跟完整的翻译键名,包括你要链接到的命名空间。

语言环境信息如下:

const messages = {
  en: {
    message: {
      the_world: 'the world',
      dio: 'DIO:',
      linked: '@:message.dio @:message.the_world !!!!'
    }
  }
}

模板如下:

<p>{{ $t('message.linked') }}</p>

输出如下:

<p>DIO: the world !!!!</p>

# 按括号分组

链接到的语言环境信息的键名也可以形如 @:(message.foo.bar.baz),其中链接到另一段翻译的键名在括号 () 里。

如果链接 @:message.something 后紧跟着一个点 .,则此选项非常有用,因为它本不该成为但却成为了链接的一部分。

语言环境信息如下:

const messages = {
  en: {
    message: {
      dio: 'DIO',
      linked: 'There\'s a reason, you lost, @:(message.dio).'
    }
  }
}

模板如下:

<p>{{ $t('message.linked') }}</p>

输出如下:

<p>There's a reason, you lost, DIO.</p>
最近一次更新: 3/27/2019, 11:07:32 AM
================================================ FILE: docs/zh/guide/number.html ================================================ 数字本地化 | Vue I18n

# 数字本地化

支持版本

🆕 7.0 新增

你可以使用你定义的格式来本地化数字。

数字格式如下:

const numberFormats = {
  'en-US': {
    currency: {
      style: 'currency', currency: 'USD'
    }
  },
  'ja-JP': {
    currency: {
      style: 'currency', currency: 'JPY', currencyDisplay: 'symbol'
    }
  }
}

如上,你可以指定具名的 (例如:currency 等) 的数字格式,并且需要使用 ECMA-402 Intl.NumberFormat 的选项

之后就像语言环境信息一样,你需要指定 VueI18n 构造函数的 numberFormats 选项:

const i18n = new VueI18n({
  numberFormats
})

new Vue({
  i18n
}).$mount('#app')

模板如下:

<div id="app">
  <p>{{ $n(100, 'currency') }}</p>
  <p>{{ $n(100, 'currency', 'ja-JP') }}</p>
</div>

输出如下:

<div id="app">
  <p>$100.00</p>
  <p>¥100</p>
</div>
最近一次更新: 3/27/2019, 11:07:32 AM
================================================ FILE: docs/zh/guide/pluralization.html ================================================ 复数 | Vue I18n

# 复数

你可以使用复数进行翻译。你必须定义具有管道 | 分隔符的语言环境,并在管道分隔符中定义复数。

语言环境信息如下:

const messages = {
  en: {
    car: 'car | cars',
    apple: 'no apples | one apple | {count} apples'
  }
}

模板如下:

<p>{{ $tc('car', 1) }}</p>
<p>{{ $tc('car', 2) }}</p>

<p>{{ $tc('apple', 0) }}</p>
<p>{{ $tc('apple', 1) }}</p>
<p>{{ $tc('apple', 10, { count: 10 }) }}</p>

输出如下:

<p>car</p>
<p>cars</p>

<p>no apples</p>
<p>one apple</p>
<p>10 apples</p>

# 通过预定义的参数访问该数字

你无需明确指定复数的数字。可以通过预定义的命名参数 {count} 和/或 {n} 在语言环境信息中访问该数字。如有必要,你可以覆盖这些预定义的命名参数。

语言环境信息如下:

const messages = {
  en: {
    apple: 'no apples | one apple | {count} apples',
    banana: 'no bananas | {n} banana | {n} bananas'
  }
}

模板如下:

<p>{{ $tc('apple', 10, { count: 10 }) }}</p>
<p>{{ $tc('apple', 10) }}</p>

<p>{{ $tc('banana', 1, { n: 1 }) }}</p>
<p>{{ $tc('banana', 1) }}</p>
<p>{{ $tc('banana', 100, { n: 'too many' }) }}</p>

输出如下:

<p>10 apples</p>
<p>10 apples</p>

<p>1 banana</p>
<p>1 banana</p>
<p>too many bananas</p>

# 自定义复数

然而,这种复数并不适用于所有语言 (例如,斯拉夫语言具有不同的复数规则)。

为了实现这些规则,你可以覆盖 VueI18n.prototype.getChoiceIndex 函数。

使用斯拉夫语言规则的简化示例 (俄语、乌克兰语等):


const defaultImpl = VueI18n.prototype.getChoiceIndex

/**
 * @param choice {number} 由 $tc 输入的选择索引:`$tc('path.to.rule', choiceIndex)`
 * @param choicesLength {number} 总体可用选择
 * @returns 选择复数单词的最终选择索引
**/
VueI18n.prototype.getChoiceIndex = function (choice, choicesLength) {
  // this === VueI18n 实例,所以语言环境属性也存在于此处
  if (this.locale !== 'ru') {
    // 继续执行默认实现
    return defaultImpl.apply(this, arguments)
  }

  if (choice === 0) {
    return 0;
  }

  const teen = choice > 10 && choice < 20;
  const endsWithOne = choice % 10 === 1;

  if (!teen && endsWithOne) {
    return 1;
  }

  if (!teen && choice % 10 >= 2 && choice % 10 <= 4) {
    return 2;
  }

  return (choicesLength < 4) ? 2 : 3;
}

这将有效地实现这一目标:

const messages = {
  ru: {
    car: '0 машин | 1 машина | {n} машины | {n} машин',
    banana: 'нет бананов | 1 банан | {n} банана | {n} бананов'
  }
}

格式为 0 things | 1 thing | few things | multiple things.

你的模板仍然需要使用 $tc(),而不是 $t()

<p>{{ $tc('car', 1) }}</p>
<p>{{ $tc('car', 2) }}</p>
<p>{{ $tc('car', 4) }}</p>
<p>{{ $tc('car', 12) }}</p>
<p>{{ $tc('car', 21) }}</p>

<p>{{ $tc('banana', 0) }}</p>
<p>{{ $tc('banana', 4) }}</p>
<p>{{ $tc('banana', 11) }}</p>
<p>{{ $tc('banana', 31) }}</p>

结果如下:

<p>1 машина</p>
<p>2 машины</p>
<p>4 машины</p>
<p>12 машин</p>
<p>21 машина</p>

<p>нет бананов</p>
<p>4 банана</p>
<p>11 бананов</p>
<p>31 банан</p>
最近一次更新: 4/10/2020, 2:30:39 AM
================================================ FILE: docs/zh/guide/sfc.html ================================================ 单文件组件 | Vue I18n

# 单文件组件

# 基本用法

如果使用单文件组件构建 Vue 组件或 Vue 应用程序,则可以管理 i18n 自定义块的语言环境信息。

以下是单文件组件示例:

<i18n>
{
  "en": {
    "hello": "hello world!"
  },
  "ja": {
    "hello": "こんにちは、世界!"
  }
}
</i18n>

<template>
  <div id="app">
    <label for="locale">locale</label>
    <select v-model="locale">
      <option>en</option>
      <option>ja</option>
    </select>
    <p>message: {{ $t('hello') }}</p>
  </div>
</template>

<script>
export default {
  name: 'app',
  data () { return { locale: 'en' } },
  watch: {
    locale (val) {
      this.$i18n.locale = val
    }
  }
}
</script>

# 安装 vue-i18n-loader

为了使用 <i18n> 自定义块,你需要安装 vue-loadervue-i18n-loader。如果你使用了单文件组件,vue-loader 很可能已在项目中使用了,那么 vue-i18n-loader 必须另外安装:

npm i --save-dev @kazupon/vue-i18n-loader

# Webpack

需要对 Webpack 进行以下配置:

对于 vue-loader v15 或更高版本:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
      },
      {
        resourceQuery: /blockType=i18n/,
        type: 'javascript/auto',
        loader: '@kazupon/vue-i18n-loader'
      }
      // ...
    ]
  },
  // ...
}

对于 vue-loader v14:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            // 你需要指定 `i18n` 的值为 `vue-i18n-loader`
            i18n: '@kazupon/vue-i18n-loader'
          }
        }
      },
      // ...
    ]
  },
  // ...
}

# Vue CLI 3.0

Vue CLI 3.0 隐藏了 webpack 配置,因此,如果我们想在单文件组件中添加对 <i18n> 标记的支持,我们需要修改现有配置。

为此,我们必须在项目的根目录下创建一个 vue.config.js。完成后,我们必须包括以下内容:

对于 vue-loader v15 或更高版本:

module.exports = {
  chainWebpack: config => {
    config.module
      .rule("i18n")
      .resourceQuery(/blockType=i18n/)
      .type('javascript/auto')
      .use("i18n")
        .loader("@kazupon/vue-i18n-loader")
        .end();
  }
}

对于 vue-loader v14:

const merge = require('deepmerge')

module.exports = {
  chainWebpack: config => {
    config.module
      .rule('vue')
      .use('vue-loader')
      .tap(options =>
        merge(options, {
          loaders: {
            i18n: '@kazupon/vue-i18n-loader'
          }
        })
      )
  }
}

别忘了安装deepmerge! (npm i deepmerge -Dyarn add deepmerge -D)

如果你想了解有关修改现有配置的更多信息点击这里

# Laravel-Mix

对于带有 vue-loader v15 或更高版本的 Laravel-mix 4:

// 使用 “i18n” 方法扩展 Mix ,加载 vue-i18n-loader
mix.extend( 'i18n', new class {
        webpackRules() {
            return [
                {
                    resourceQuery: /blockType=i18n/,
                    type:          'javascript/auto',
                    loader:        '@kazupon/vue-i18n-loader',
                },
            ];
        }
    }(),
);

// 确保在 `.js(..., ...)` 之前调用 `.i18n()` (来加载加载器)
mix.i18n()
   .js( 'resources/js/App.js', 'public/js/app.js' )
   ...

对于带有 vue-loader v14 的 Laravel-mix 2:

从 Laravel-mix 的 V2.1 开始,你可以通过 mix.extend() 添加自定义规则。Laravel mix 已经有了处理 .vue 文件的规则。要添加 vue-i18n-loader,请将以下内容添加到 webpack.mix.js

// 下面的代码将注入 i18n Kazupon/vue-18-loader 作为 .vue 文件的加载器。
mix.extend( 'i18n', function( webpackConfig, ...args ) {
    webpackConfig.module.rules.forEach( ( module ) => {
        // 搜索处理 .vue 文件的 “vue-loader” 组件。
        if( module.loader !== 'vue-loader' ) {
            return;
        }

        // 在此模块中,为 i18n 标记添加 vue-i18n-loader。
        module.options.loaders.i18n = '@kazupon/vue-i18n-loader';
    } );
} );

// 确保在 `.js(...,...)` 之前调用 `.i18n()`
mix.i18n()
   .js( 'resources/assets/js/App.js', 'public/js/app.js' )
   ...

# 加载 YAML

i18n 自定义块需要指定为 JSON 格式,你也可以通过使用 vue-loader 预加载器功能来使用 YAML 格式。

以下是 YAML 格式的 i18n 自定义块:

<i18n>
en:
  hello: "hello world!"
ja:
  hello: "こんにちは、世界!"
</i18n>

Webpack 配置如下:

对于 vue-loader v15 或更高版本:

// Vue CLI 3.0
module.exports = {
  chainWebpack: config => {
    config.module
      .rule("i18n")
      .resourceQuery(/blockType=i18n/)
      .type('javascript/auto')
      .use("i18n")
        .loader("@kazupon/vue-i18n-loader")
        .end()
      .use('yaml')
        .loader('yaml-loader')
        .end()
  }
}

对于 vue-loader v14:

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          preLoaders: {
            i18n: 'yaml-loader'
          },
          loaders: {
            i18n: '@kazupon/vue-i18n-loader'
          }
        }
      },
      // ...
    ]
  },
  // ...
}

# 多个自定义块

你可以使用具有多个 i18n 自定义块的语言环境信息。

<i18n src="./common/locales.json"></i18n>
<i18n>
  {
    "en": {
      "hello": "hello world!"
    },
    "ja": {
      "hello": "こんにちは、世界!"
    }
  }
</i18n>

如上所见,第一个自定义块使用 src 特性加载常用的语言环境信息,第二个自定义块加载仅在该单文件组件中定义的语言环境信息。这些语言环境信息将合并为组件的语言环境信息。

这样,多个自定义块在想要用作模块时非常有用。

# Scoped 风格

当使用带有 scoped style vue-i18n 时,重要的是要记住使用深度选择器 来设置嵌套转换的样式。例如:

...
<template>
  <div class="parent">
    <p>message: {{ $t('hello') }}</p>
  </div>
</template>
...
<!-- 不可行-->
<style scoped>
.parent p {
  color: #42b883;
}
</style>

<!-- 可行 -->
<style>
.parent >>> p {
  color: #42b883;
}
</style>

# 函数式组件中的自定义块

如果单个文件组件具有使用函数式组件的模板,并且你已经定义了 i18n 自定义块,请注意你无法使用语言环境信息进行本地化。

例如,以下代码无法使用 i18n 自定义块的语言环境信息进行本地化。

<i18n>
{
  "en": {
    "hello": "hello world"
  },
  "ja": {
    "hello": "こんにちは、世界"
  }
}
</i18n>

<template functional>
  <!-- 'hello' 的父实例的语言环境信息 -->
  <p>{{ parent.$t('hello') }}</p>
</template>
最近一次更新: 3/27/2019, 11:07:32 AM
================================================ FILE: docs/zh/guide/tooling.html ================================================ 工具 | Vue I18n

# 工具

为了支持开发,我们官方提供了一些工具。

此外,还有第三方供应商提供的集成了 Vue I18n 的工具。

# 官方工具

# Vue Cli 插件

vue-cli-plugin-i18n 是官方提供的 Vue Cli 插件。

使用此插件,您可以为Vue应用程序设置 i18n 环境,并支持 i18n 开发环境。

# Webpack Loader

vue-i18n-loader 是官方提供的 Webpack Loader。 使用此加载程序,您可以在单个文件组件中使用 i18n 自定义块。

关于 i18n 自定义块,请参见 单文件组件

# ESLint 插件

eslint-plugin-vue-i18n 是为 Vue I18n 编写的 ESLint 插件。

它可以轻松地将一些本地化 lint 功能集成到 Vue.js 应用程序中。

# Extensions

vue-i18n-extensions 你可以找到为 Vue I18n 编写的插件。

您可以使用此扩展来启用 SSR 并提高 Vue I18n 的性能。

# 第三方工具

# BabelEdit

BabelEdit 是 Web 应用程序的翻译编辑器。

BabelEdit可以翻译 json 文件,也可以翻译单文件组件的 i18n 自定义块。

欲了解更多,请看教程

最近一次更新: 3/19/2020, 2:28:38 AM
================================================ FILE: docs/zh/index.html ================================================ Vue I18n ================================================ FILE: docs/zh/installation.html ================================================ 安装 | Vue I18n

# 安装

# 兼容性说明

  • Vue.js 2.0.0+

# 直接下载 / CDN

https://unpkg.com/vue-i18n/dist/vue-i18n

unpkg.com 提供了基于 NPM 的 CDN 链接。上面的链接会一直指向在 NPM 发布的最新版本。你也可以通过 https://unpkg.com/vue-i18n@8.9.0/dist/vue-i18n.js 这样的 URL 指定版本号或者 tag。

在 Vue 之后引入 vue-i18n,它会自动安装:

<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-i18n/dist/vue-i18n.js"></script>

# NPM

npm install vue-i18n

# Yarn

yarn add vue-i18n

如果在一个模块系统中使用它,你必须通过 Vue.use() 明确地安装 vue-i18n

import Vue from 'vue'
import VueI18n from 'vue-i18n'

Vue.use(VueI18n)

如果使用全局的 script 标签,则无须如此 (手动安装)。

# Vue Cli 3.x

vue add i18n

你需要 Vue cli 3.x 作为先决条件,你可以在命令行上使用下面的命令来安装:

npm install @vue/cli -g

# 开发版构建

如果你想使用最新的开发版构建,就得从 GitHub 上直接 clone,然后自己构建一个 vue-i18n

git clone https://github.com/kazupon/vue-i18n.git node_modules/vue-i18n
cd node_modules/vue-i18n
npm install # or `yarn`
npm run build  # or `yarn run build`
最近一次更新: 3/27/2019, 11:07:32 AM
================================================ FILE: docs/zh/introduction.html ================================================ 介绍 | Vue I18n

# 介绍

说明

⚠️ 本文档适用于 Vue I18n v6.0 或更高版本. 如果你使用 v5.x,请参阅旧版部分。

Vue I18n 是 Vue.js 的国际化插件。它可以轻松地将一些本地化功能集成到你的 Vue.js 应用程序中。

让我们开始吧

# 赞助商

# 白银赞助商

# 成为一名 Patreon 赞助商

贵公司是否使用 vue-i18n 或 vue-cli-plugin-i18n 来构建出色的应用程序? 加入其他会员并成为赞助商,在此文档中添加你的徽标!在 Patreon 上支持我使我能够更少地完成工作并在免费开源软件上工作,例如 vue-i18n! 谢谢!

Become a Patreon

最近一次更新: 4/29/2019, 6:44:18 PM
================================================ FILE: docs/zh/legacy/index.html ================================================ 从 v5.x 迁移 | Vue I18n

# 从 v5.x 迁移

# 全局配置

# lang 已被替换

使用 VueI18n 类构造函数 locale 选项或 VueI18n#locale 属性:

  const i18n = new VueI18n({
    locale: 'en',
    // ...
  })
  const app = new Vue({ i18n }).$mount('#app')

  // 更改 locale
  i18n.locale = 'ja'
  // 或者
  app.$i18n.locale = 'ja'

# fallbackLang 已被替换

使用 VueI18n 类构造函数 fallbackLocale 选项或 VueI18n#fallbackLocale 属性:

  const i18n = new VueI18n({
    locale: 'ja',
    fallbackLocale: 'en',
    // ...
  })
  const app = new Vue({ i18n }).$mount('#app')

  // 更改 fallback locale
  i18n.fallbackLocale = 'zh'
  // 或者
  app.$i18n.fallbackLocale = 'zh'

# missingHandler 已被替换

使用 VueI18n 类构造函数 missing 选项或 VueI18n#missing 属性:

const i18n = new VueI18n({
  // ...
  missing: (locale, key, vm) => {
    // 处理翻译缺失
  },
  // ...
})
const app = new Vue({ i18n }).$mount('#app')

// 改变丢失的处理函数
i18n.missing = (locale, key, vm) => {
  // 处理翻译缺失
}
// or
app.$i18n.missing = (locale, key, vm) => {
  // 处理翻译缺失
}

# i18nFormatter 已被替换

使用 VueI18n 类构造函数 formatter 选项或 VueI18n#formatter 属性:

  class CustomFormatter {
    format (message, ...values) {
      // 一些渲染逻辑
      return 'something string'
    }
  }

  const i18n = new VueI18n({
    // ...
    formatter: new CustomFormatter(),
    // ...
  })
  const app = new Vue({ i18n }).$mount('#app')

  // 更改自定义格式化程序
  i18n.formatter = {
    format: (message, ...values) => {
      // 一些渲染逻辑
      return 'something string'
    }
  }
  // or
  app.$i18n.formatter = {
    format: (message, ...values) => {
      // 一些渲染逻辑
      return 'something string'
    }
  }

# 全局方法

# Vue.locale 已被替换

使用 VueI18n 类构造函数 messages 选项,或者使用 VueI18n#GetLocaleMessage / VueI18n#setLocaleMessage 方法:

  const i18n = new VueI18n({
    // ...
    messages: {
      en: {
        hello: 'hello world',
        // ...
      },
      ja: {
        hello: 'こんにちは、世界',
        // ...
      }
    },
    // ...
  })
  const app = new Vue({ i18n }).$mount('#app')

  // 获取 locale message
  const en = i18n.getLocaleMessage('en')
  en.greeting = 'hi!'
  // 设置 locale message
  i18n.setLocaleMessage('en', en)
  // 或者
  const ja = app.$i18n.getLocaleMessage('ja')
  ja.greeting = 'やあ!'
  app.$i18n.setLocaleMessage('ja', ja)

# Vue.t 已被替换

使用 VueI18n#t 方法:

const i18n = new VueI18n({
  locale: 'en',
  messages: {
    en: {
      greeting: 'hi {name}'
    }
  },
  // ...
})

i18n.t('greeting', { name: 'kazupon' }) // -> hi kazupon

# Vue.tc 已被替换

使用 VueI18n#tc 方法:

const i18n = new VueI18n({
  locale: 'en',
  messages: {
    en: {
      apple: 'no apples | one apple | {count} apples'
    }
  },
  // ...
})

const count = 10
i18n.tc('apple', count, { count }) // -> 10 apples

# Vue.te 已被替换

使用 VueI18n#te 方法:

const i18n = new VueI18n({
  locale: 'en',
  messages: {
    en: {
      hello: 'hello world'
    }
  },
  // ...
})

i18n.te('hello') // -> true
i18n.te('hallo', 'ja') // -> false
i18n.te('hello') // -> true

# 构造函数选项

# locales 已被替换

使用 messagesVueI18n 类的构造函数的选项,或 messagesi18n 选项 (针对组件选项):

const i18n = new VueI18n({
  locale: 'en',
  messages: {
    en: {
      greeting: 'hi {name}'
    }
  },
  // ...
})

// 针对组件选项
const Component1 = {
  i18n: {
    messages: {
      en: {
        title: 'Title1'
      }
    }
  }
}

# 实例属性

# $lang 已被替换

使用 VueI18n#locale 属性:

const i18n = new VueI18n({
  locale: 'en',
  // ...
})
const app = new Vue({ i18n }).$mount('#app')

// 更改 locale
i18n.locale = 'ja'
// 或者
app.$i18n.locale = 'ja'

# 特性

# 已删除动态语言环境

如果需要动态设置语言环境信息,则应实现以下内容:

const i18n = new VueI18n({ locale: 'en' })
const app = new Vue({
  i18n,
  data: { loading: '' }
}).$mount('#app')

function loadLocaleMessage (locale, cb) {
  return fetch('/locales/' + locale, {
    method: 'get',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    }
  }).then((res) => {
    return res.json()
  }).then((json) => {
    if (Object.keys(json).length === 0) {
      return Promise.reject(new Error('locale empty !!'))
    } else {
      return Promise.resolve(json)
    }
  }).then((message) => {
    cb(null, message)
  }).catch((error) => {
    cb(error)
  })
}

app.loading = 'loading ...'
loadLocaleMessage('en', (err, message) => {
  if (err) {
    app.loading = ''
    console.error(err)
    return
  }
  i18n.setLocaleMessage('en', message)
  app.loading = ''
})
最近一次更新: 3/27/2019, 11:07:32 AM
================================================ FILE: docs/zh/legacy/v5.html ================================================ v5.x 版本的文档 | Vue I18n ================================================ FILE: docs/zh/started.html ================================================ 开始 | Vue I18n

# 开始

说明

我们将在指南中的代码示例中使用 ES2015 语法。

# HTML

<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-i18n/dist/vue-i18n.js"></script>

<div id="app">
  <p>{{ $t("message.hello") }}</p>
</div>

# JavaScript

// 如果使用模块系统 (例如通过 vue-cli),则需要导入 Vue 和 VueI18n ,然后调用 Vue.use(VueI18n)。
// import Vue from 'vue'
// import VueI18n from 'vue-i18n'
//
// Vue.use(VueI18n)

// 准备翻译的语言环境信息
const messages = {
  en: {
    message: {
      hello: 'hello world'
    }
  },
  ja: {
    message: {
      hello: 'こんにちは、世界'
    }
  }
}

// 通过选项创建 VueI18n 实例
const i18n = new VueI18n({
  locale: 'ja', // 设置地区
  messages, // 设置地区信息
})


// 通过 `i18n` 选项创建 Vue 实例
new Vue({ i18n }).$mount('#app')

// 现在应用程序已经准备好了!

输出如下:

<div id="#app">
  <p>こんにちは、世界</p>
</div>
最近一次更新: 3/27/2019, 11:07:32 AM
================================================ FILE: examples/.eslintrc.js ================================================ module.exports = { root: true, parserOptions: { ecmaVersion: 2018, }, extends: [ 'plugin:vue-libs/recommended', ], } ================================================ FILE: examples/component/index.html ================================================ component based translation

{{ $t("message.hello") }}

================================================ FILE: examples/datetime/index.html ================================================ datetime localization

{{ $t('current_datetime')}}: {{ $d(now, 'long') }}

{{ $t('past_datetime')}}: {{ $d(pastDatetime, 'long') }}

{{ $t('past_year')}}: {{ $d(pastDatetime, {year: '2-digit'}) }}

================================================ FILE: examples/directive/index.html ================================================ custom directive example

================================================ FILE: examples/es-modules/index.html ================================================ ES modules browser example

{{ $t('message.hello') }}

================================================ FILE: examples/formatting/custom/.gitignore ================================================ .DS_Store node_modules /dist # local env files .env.local .env.*.local # Log files npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.sw? ================================================ FILE: examples/formatting/custom/README.md ================================================ # vue-i18n-custom-formatter-example Custom formatter example for vue-i18n > :warning: You must use [the ECMA-402 (Intl) support browser](http://kangax.github.io/compat-table/esintl/) due to try this example ## Project setup ``` npm install ``` ### Compiles and hot-reloads for development ``` npm run serve ``` ### Compiles and minifies for production ``` npm run build ``` ### Customize configuration See [Configuration Reference](https://cli.vuejs.org/config/). ================================================ FILE: examples/formatting/custom/babel.config.js ================================================ module.exports = { presets: [ '@vue/app' ] } ================================================ FILE: examples/formatting/custom/package.json ================================================ { "name": "vue-i18n-custom-formatter-example", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build" }, "dependencies": { "core-js": "^3.6.4", "messageformat": "^3.0.0-beta.1", "vue": "^2.6.11", "vue-i18n": "^8.15.3" }, "devDependencies": { "@vue/cli-plugin-babel": "^4.2.2", "@vue/cli-service": "^4.2.2", "vue-template-compiler": "^2.6.11" }, "postcss": { "plugins": { "autoprefixer": {} } }, "browserslist": [ "> 1%", "last 2 versions" ] } ================================================ FILE: examples/formatting/custom/public/index.html ================================================ custom
================================================ FILE: examples/formatting/custom/src/App.vue ================================================ ================================================ FILE: examples/formatting/custom/src/formatter.js ================================================ import MessageFormat from 'messageformat' export default class CustomFormatter { constructor (options = {}) { this._locale = options.locale || 'en-US' this._formatter = new MessageFormat(this._locale, {returnType: 'values'}) this._caches = Object.create(null) } interpolate (message, values) { let fn = this._caches[message] if (!fn) { fn = this._formatter.compile(message, this._locale) this._caches[message] = fn } if (values !== null && values !== undefined) { /** * Component interpolation in the following format passes `values` as an Array-like `Object` * * ```vue * * I agree with the {{ $t('tos') }} * * ``` * * VNodes _may_ be found when using interpolated values with either the deprecated `places` **or** the new slot * syntax */ const valuesEntries = Object.entries(values) const isNonSlotInterpolation = valuesEntries.some(entry => { const [key, value] = entry return value.hasOwnProperty('componentOptions') && isNaN(Number.parseFloat(key)) === false }) if (isNonSlotInterpolation) { /** * In this case, we iterate over the VNode list and preserve **only** VNodes with tags and return them, wrapped * in a list. * * Rationale: If text nodes are kept, the positional lookup from vue-i18n will fail, rendering the first * child again (i.e `I agree with the, I agree with the`) */ values = valuesEntries.map(entry => { const [, vnode] = entry if (vnode.tag !== undefined) { return [vnode] } }).filter(Boolean) } } const result = fn(values).map(piece => { if (!Array.isArray(piece)) { return piece } const hasVNodes = piece.some(part => part.hasOwnProperty('componentOptions')) if (hasVNodes === false) { return piece.join('') } return piece }) return result } } ================================================ FILE: examples/formatting/custom/src/i18n.js ================================================ import Vue from 'vue' import VueI18n from 'vue-i18n' import Formatter from './formatter' Vue.use(VueI18n) const locale = 'en-US' // default locale const formatter = new Formatter({ locale }) export default new VueI18n({ locale, formatter, messages: { 'en-US': { tos: 'Terms of service', terms: 'I accept the {0}', message: { hello: 'hello!!', plural: 'You have {n, plural, =0{no messages} one{1 message} other{# messages}}.', select: '{gender, select, male{He} female{She} other{They}} liked this.', number: 'Current Percent: {current, number, percent}', time: 'Current Time: {current, time, short}', }, fly: { departure: 'You can {action} until {limit} minutes from departure', board: 'board' } }, 'ja-JP': { message: { hello: 'こんにちは!!', select: '{gender, select, male{彼} female{彼女} other{彼ら}} はこれを好きです。', number: '現在パーセンテージ {current, number, percent}', time: '現在時刻: {current, time, medium}', } } } }) ================================================ FILE: examples/formatting/custom/src/main.js ================================================ import Vue from 'vue' import App from './App.vue' import i18n from './i18n' new Vue({ i18n, render: h => h(App) }).$mount('#app') ================================================ FILE: examples/formatting/escape-parameter-html/index.html ================================================ Escape parameter HTML example

================================================ FILE: examples/formatting/html/index.html ================================================ html formatting example

================================================ FILE: examples/formatting/html-warning/index.html ================================================ HTML locale message warning option example

{{ $t('html_message') }}

================================================ FILE: examples/formatting/list/index.html ================================================ list formatting example

{{ $t('message.hello', ["hello"]) }}

================================================ FILE: examples/formatting/named/index.html ================================================ named formatting example

{{ $t('message.hello', { msg: "hello"}) }}

================================================ FILE: examples/formatting/rails/index.html ================================================ ruby on rails formatting example

{{ $t('message.hello', { msg: "hello"}) }}

================================================ FILE: examples/hot/.gitignore ================================================ .DS_Store node_modules /dist # local env files .env.local .env.*.local # Log files npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.sw? ================================================ FILE: examples/hot/README.md ================================================ # vue-i18n-hot-module-replacement ## Project setup ``` npm install ``` ### Compiles and hot-reloads for development ``` npm run serve ``` ### Compiles and minifies for production ``` npm run build ``` ### Customize configuration See [Configuration Reference](https://cli.vuejs.org/config/). ================================================ FILE: examples/hot/babel.config.js ================================================ module.exports = { presets: [ '@vue/app' ] } ================================================ FILE: examples/hot/package.json ================================================ { "name": "vue-i18n-hot-module-replacement", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build" }, "dependencies": { "core-js": "^2.6.5", "vue": "^2.6.10", "vue-i18n": "^8.11.2" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.8.0", "@vue/cli-service": "^3.8.0", "vue-template-compiler": "^2.6.10" }, "postcss": { "plugins": { "autoprefixer": {} } }, "browserslist": [ "> 1%", "last 2 versions" ] } ================================================ FILE: examples/hot/public/index.html ================================================ hot
================================================ FILE: examples/hot/src/App.vue ================================================ ================================================ FILE: examples/hot/src/components/HelloWorld.vue ================================================ ================================================ FILE: examples/hot/src/i18n.js ================================================ import Vue from 'vue' import VueI18n from 'vue-i18n' Vue.use(VueI18n) function loadLocaleInfo () { const locales = require.context('./locales', true, /(^|\/)[A-Za-z0-9_,\s-]+\.json$/i) const messages = {} locales.keys().forEach(key => { const matched = key.match(/([A-Za-z0-9_-]+)\./i) if (matched && matched.length > 1) { const locale = matched[1] messages[locale] = locales(key) } }) return { id: locales.id, messages } } const { id, messages } = loadLocaleInfo() const i18n = new VueI18n({ locale: 'en', messages }) if (module.hot) { module.hot.accept(id, () => { const { messages } = loadLocaleInfo() Object.keys(messages).forEach(locale => { i18n.setLocaleMessage(locale, messages[locale]) }) console.log('hot module replacemenet', arguments) }) } export default i18n ================================================ FILE: examples/hot/src/locales/en.json ================================================ { "hello": "Hello World!" } ================================================ FILE: examples/hot/src/locales/ja.json ================================================ { "hello": "こんにちは、世界!" } ================================================ FILE: examples/hot/src/main.js ================================================ import Vue from 'vue' import App from './App.vue' import i18n from './i18n' Vue.config.productionTip = false new Vue({ i18n, render: h => h(App) }).$mount('#app') ================================================ FILE: examples/integration/contentful/.contentful.sample.json ================================================ { "CTF_PERSON_ID": "15jwOBqpxqSAOy2eOO4S0m", "CTF_BLOG_POST_TYPE_ID": "blogPost", "CTF_SPACE_ID": "SPACE_ID", "CTF_CDA_ACCESS_TOKEN": "CDA_ACCESS_TOKEN", "CTF_CMA_ACCESS_TOKEN": "CMA_ACCESS_TOKEN" } ================================================ FILE: examples/integration/contentful/.editorconfig ================================================ # editorconfig.org root = true [*] indent_size = 2 indent_style = space end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = true ================================================ FILE: examples/integration/contentful/.eslintrc.js ================================================ module.exports = { root: true, parser: 'babel-eslint', env: { browser: true, node: true }, extends: 'standard', // required to lint *.vue files plugins: [ 'html' ], // add your custom rules here rules: {}, globals: {} } ================================================ FILE: examples/integration/contentful/.gitignore ================================================ ### Linux ### *~ # temporary files which can be created if a process still has a handle open of a deleted file .fuse_hidden* # KDE directory preferences .directory # Linux trash folder which might appear on any partition or disk .Trash-* # .nfs files are created when an open file is removed but is still being accessed .nfs* ### Node ### # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # Typescript v1 declaration files typings/ # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env ### OSX ### *.DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ### Windows ### # Windows thumbnail cache files Thumbs.db ehthumbs.db ehthumbs_vista.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # Windows Installer files *.cab *.msi *.msm *.msp # Windows shortcuts *.lnk ############################################ # 'blog in 5 minutes' files ############################################ # Nuxt build .nuxt # Nuxt generate dist # Contentful configs .contentful.json # data data ================================================ FILE: examples/integration/contentful/LICENSE ================================================ MIT License Copyright (c) 2017 Stefan Judis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: examples/integration/contentful/README.md ================================================ # vue-i18n for contentful > Example for Contentful integration ## Setup ### Setup contentful with tutorial https://www.contentful.com/developers/docs/tutorials/general/get-started/ ### Run ``` $ npm run dev ``` ## License MIT ================================================ FILE: examples/integration/contentful/WHATS-NEXT.md ================================================ # What's next? A blog on your local machine is nice to have, but a blog on the internet is even better. ## Deploying using now.sh Change directory to where you installed your application and run: `npm run deploy` ## Other deployment options Nuxt.js also offers guides to deploy your project to: - [Heroku](https://nuxtjs.org/faq/heroku-deployment) - [gh-pages](https://nuxtjs.org/faq/github-pages) - [surge.sh](https://nuxtjs.org/faq/surge-deployment) - [dokku](https://nuxtjs.org/faq/dokku-deployment) Enjoy! ================================================ FILE: examples/integration/contentful/assets/README.md ================================================ # ASSETS This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. More information about the usage of this directory in the documentation: https://nuxtjs.org/guide/assets#webpacked **This directory is not required, you can delete it if you don't want to use it.** ================================================ FILE: examples/integration/contentful/bin/download-content-model.js ================================================ const fs = require('fs') const path = require('path') const zlib = require('zlib') const fetch = require('node-fetch') const Listr = require('listr') const tar = require('tar') const mkdirp = require('mkdirp') const rimraf = require('rimraf') const REPO_ROOT = path.resolve(__dirname, '..') const DATA_DIR = path.resolve(REPO_ROOT, 'data') const tasks = new Listr([ { title: `Checking permissions to write files to the disk`, task: (ctx) => { return new Promise((resolve, reject) => { fs.access(REPO_ROOT, fs.constants.W_OK, (err) => { if (err) { reject() console.error(`Script is unable to write to project directory. Please check your users file permissions for ${REPO_ROOT}`) process.exit(1) } resolve() }) }) } }, { title: `Clean up directory`, task: (ctx) => { return new Promise((resolve, reject) => { rimraf(DATA_DIR, (err) => { if (err) { reject(err) } resolve() }) }).then(() => { return new Promise((resolve, reject) => { mkdirp(DATA_DIR, (err) => { if (err) { reject(err) } resolve() }) }) }) } }, { title: `Fetching release information of contentful/content-models`, task: (ctx) => { return fetch(`https://api.github.com/repos/contentful/content-models/releases/latest`) .then((response) => response.json()) .then((json) => { ctx.latestReleaseInfo = json }) } }, { title: `Downloading latest release of contentful/content-models`, task: (ctx) => { ctx.latestReleaseZipLocation = path.join(DATA_DIR, 'latest-release.tar.gz') return fetch(ctx.latestReleaseInfo.tarball_url) .then((response) => { ctx.latestReleaseTarballStream = response.body }) } }, { title: `Unpacking latest release of contentful/content-models`, task: (ctx) => { return new Promise((resolve, reject) => { try { ctx.latestReleaseTarballStream .pipe(zlib.Unzip()) .pipe(new tar.Unpack({ cwd: DATA_DIR, strip: 1 })) .on('error', reject) .on('close', resolve) } catch (err) { reject(err) } }) } } ]) tasks.run() ================================================ FILE: examples/integration/contentful/components/README.md ================================================ # COMPONENTS The components directory contains your Vue.js Components. Nuxt.js doesn't supercharge these components. **This directory is not required, you can delete it if you don't want to use it.** ================================================ FILE: examples/integration/contentful/components/article-preview.vue ================================================ ================================================ FILE: examples/integration/contentful/components/language-header.vue ================================================ ================================================ FILE: examples/integration/contentful/components/navigation.vue ================================================ ================================================ FILE: examples/integration/contentful/docs/GETTING-STARTED.md ================================================ # You don't have to set this up yourself The following steps describe what is needed to set this project up manually. **You can also follow our [Interactive getting started CLI tutorial](https://www.contentful.com/developers/docs/tutorials/general/get-started/) to save some time.** ;) ## Getting started To get you started you have to be registered at [Contentful](https://www.contentful.com). ### Clone the repo and install dependencies ```bash $ git clone git@github.com:contentful/blog-in-5-minutes.git $ cd blog-in-5-minutes $ npm install ``` ### Get needed config data from Contentful To set up and use a new space you have to create and retrieve the needed access tokens. So let's jump right into it... #### Create a new space You can create a new space by pressing in the space overview in the top left corner of the web app. ![Create new space dialog](./images/login.png "Create new space dialog") #### Create a Content Management API token To create a new Content Management API token navigate to `APIs` in the top level menu and then to `Content Managements Tokens`. ![Dialog to create a CMA token](./images/cma-token.png "Dialog to create a CMA token") *You'll need Content Management API token to write the provided data into your space.* #### Create a Content Delivery API token and get space ID To create a new Content Management API token navigate to `APIs` in the top level menu and then to `Content Delivery / Preview tokens`. ![Dialog to create a CDA token](./images/cda-create-token.png "Dialog to create a CDA token") ![Dialog to copy a CDA token](./images/cda-copy-token.png "Dialog to copy a CDA token") *You'll need the Content Delivery API token to access the data stored in your space.* ### Import data in your newly created space To import the content model and data you can use a tool from the Contentful ecosystem - [contentful-import](https://www.npmjs.com/package/contentful-import). This tool allows you to set up a new space with before exported data. It requires at least a Content Management API token and the id of the space that should be filled with data. To make it not necessary to install contentful-import globally it's defined as a development dependency which means that you can use it via an npm scripts command. Run `npm run import-data` with the additional parameters. *Side note: `--` is needed hand npm scripts arguments over to the actual command* ```bash $ npm run import-data -- --space-id YOUR_SPACE_ID --management-token YOUR_MANAGEMENT_TOKEN ``` ### Create a config file Now that you have the Contentful setup done, it's time to define the configuration to run the project. To make this project work locally you have to rename the `.contentful.sample.json` in the root of the directory to `.contentful.json`. You have to set two values: Needed config data: - the ID of the space where your data lives - `CTF_SPACE_ID` - the Content Delivery API token so that you can fetch the data - `CTF_CDA_ACCESS_TOKEN` - the Content Management API token so that you can fetch the valid tags - `CTF_CMA_ACCESS_TOKEN` ```javascript { // these values are already correct "CTF_PERSON_ID": "15jwOBqpxqSAOy2eOO4S0m", "CTF_BLOG_POST_TYPE_ID": "blogPost", // these values have to be defined by you "CTF_SPACE_ID": "YOUR_SPACE_ID", "CTF_CDA_ACCESS_TOKEN": "YOUR_DELIVERY_ACCESS_TOKEN", "CTF_CMA_ACCESS_TOKEN": "YOUR_MANAGEMENT_ACCESS_TOKEN" }; ``` **Remember:** Comments are not allowed in JSON so make sure to remove them. ;) ### Preview your the site To see the site working with your newly created data all you have to do is to run `npm run dev`. This command starts a server at `localhost:3000`. ### Deploy the site to `now` `now` is a fairly new service by [zeit](https://zeit.co), which also provides static hosting. If you run `npm run deploy` you're asked for your email adress (which you have to confirm) once and then your site will find its way into the cloud. ```bash $ npm run deploy ``` ================================================ FILE: examples/integration/contentful/layouts/README.md ================================================ # LAYOUTS This directory contains your Application Layouts. More information about the usage of this directory in the documentation: https://nuxtjs.org/guide/views#layouts **This directory is not required, you can delete it if you don't want to use it.** ================================================ FILE: examples/integration/contentful/layouts/default.vue ================================================ ================================================ FILE: examples/integration/contentful/locales/en-US.json ================================================ { "components": { "language": { "current": "Current language code: {language}" }, "navigation": { "menu": { "blog": "Blog Articles", "index": "Index" } } }, "pages": { "top": { "recently": "Recent articles" }, "blog": { "title": "Blog", "subtitle": "All articles ({count})" }, "tags": { "subtitle": "All articles tagged #{tag} ({count})" } } } ================================================ FILE: examples/integration/contentful/locales/ja.json ================================================ { "components": { "language": { "current": "現在の言語コード: {language}" }, "navigation": { "menu": { "blog": "ブログ一覧", "index": "インデックス" } } }, "pages": { "top": { "recently": "最近の記事" }, "blog": { "title": "ブログ一覧", "subtitle": "全ての記事 ({count})" }, "tags": { "subtitle": "#{tag} でタグ付けされた全ての記事 ({count})" } } } ================================================ FILE: examples/integration/contentful/middleware/README.md ================================================ # MIDDLEWARE This directory contains your Application Middleware. The middleware lets you define custom function to be ran before rendering a page or a group of pages (layouts). More information about the usage of this directory in the documentation: https://nuxtjs.org/guide/routing#middleware **This directory is not required, you can delete it if you don't want to use it.** ================================================ FILE: examples/integration/contentful/nuxt.config.js ================================================ const {getConfigForKeys} = require('./lib/config.js') const ctfConfig = getConfigForKeys([ 'CTF_BLOG_POST_TYPE_ID', 'CTF_SPACE_ID', 'CTF_CDA_ACCESS_TOKEN', 'CTF_CMA_ACCESS_TOKEN', 'CTF_PERSON_ID' ]) const {createClient} = require('./plugins/contentful') const cdaClient = createClient(ctfConfig) const cmaContentful = require('contentful-management') const cmaClient = cmaContentful.createClient({ accessToken: ctfConfig.CTF_CMA_ACCESS_TOKEN }) const config = { /* ** Headers of the page */ head: { title: 'Custom app with Contentful', meta: [ { charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { hid: 'description', name: 'description', content: 'Custom app in under 5 minutes' } ], link: [ { rel: 'icon', type: 'image/x-icon', href: '/favicon.png' }, { rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/minireset.css/0.0.2/minireset.min.css' } ] }, /* ** Customize the progress-bar color */ loading: { color: '#2199e8' }, /* ** Build configuration */ build: { /* ** Run ESLINT on save */ extend (config, ctx) { if (ctx.isClient) { config.module.rules.push({ enforce: 'pre', test: /\.(js|vue)$/, loader: 'eslint-loader', exclude: /(node_modules)/ }) } }, postcss: [ require('autoprefixer')({ browsers: ['> 5%'] }) ] }, /* ** ᕕ( ᐛ )ᕗ CTF-BLOG-IN-5-MINUTES ** Make client available everywhere via Nuxt plugins */ plugins: [ '~/plugins/contentful', '~/plugins/i18n' ], /* ** ᕕ( ᐛ )ᕗ CTF-BLOG-IN-5-MINUTES ** Get all blog posts from Contentful ** and generate the needed files upfront ** ** Included: ** - blog posts ** - available blog post tags */ generate: { routes () { return Promise.all([ // get all blog posts cdaClient.getEntries({ 'content_type': ctfConfig.CTF_BLOG_POST_TYPE_ID }), // get the blog post content type cmaClient.getSpace(ctfConfig.CTF_SPACE_ID) .then(space => space.getContentType(ctfConfig.CTF_BLOG_POST_TYPE_ID)) ]) .then(([entries, postType]) => { return [ // map entries to URLs ...entries.items.map(entry => `/blog/${entry.fields.slug}`), // map all possible tags to URLs ...postType.fields.find(field => field.id === 'tags').items.validations[0].in.map(tag => `/tags/${tag}`) ] }) } }, /* ** Define environment variables being available ** in generate and browser context */ env: { CTF_SPACE_ID: ctfConfig.CTF_SPACE_ID, CTF_CDA_ACCESS_TOKEN: ctfConfig.CTF_CDA_ACCESS_TOKEN, CTF_PERSON_ID: ctfConfig.CTF_PERSON_ID, CTF_BLOG_POST_TYPE_ID: ctfConfig.CTF_BLOG_POST_TYPE_ID } } module.exports = config ================================================ FILE: examples/integration/contentful/package.json ================================================ { "name": "contentful-blog-in-5-min", "version": "1.1.1", "description": "A static blog based on Contentful", "author": "stefan judis ", "private": true, "dependencies": { "contentful": "^4.3.0", "contentful-management": "^3.4.0", "nuxt": "^1.0.0-rc4", "vue-i18n": "^7.6.0", "vue-markdown": "^2.1.3" }, "scripts": { "dev": "nuxt", "build": "nuxt build", "start": "nuxt start", "generate": "nuxt generate", "lint": "eslint --ext .js,.vue --ignore-path .gitignore .", "precommit": "npm run lint", "deploy": "nuxt generate && now dist", "import-data": "node ./bin/download-content-model.js && contentful-import --content-file ./data/blog/contentful-export.json" }, "devDependencies": { "babel-eslint": "^7.1.1", "contentful-import": "^4.5.4", "cz-conventional-changelog": "^2.0.0", "eslint": "^4.18.2", "eslint-config-standard": "^6.2.1", "eslint-loader": "^1.6.1", "eslint-plugin-html": "^2.0.0", "eslint-plugin-promise": "^3.4.1", "eslint-plugin-standard": "^2.0.1", "gh-pages": "^1.0.0", "listr": "^0.12.0", "node-fetch": "^3.1.1", "now": "^5.3.0", "rimraf": "^2.6.1", "tar": "^4.4.18" }, "config": { "commitizen": { "path": "./node_modules/cz-conventional-changelog" } } } ================================================ FILE: examples/integration/contentful/pages/README.md ================================================ # PAGES This directory contains your Application Views and Routes. The framework reads all the .vue files inside this directory and create the router of your application. More information about the usage of this directory in the documentation: https://nuxtjs.org/guide/routing ================================================ FILE: examples/integration/contentful/pages/blog/_slug.vue ================================================ ================================================ FILE: examples/integration/contentful/pages/blog/index.vue ================================================ ================================================ FILE: examples/integration/contentful/pages/index.vue ================================================ ================================================ FILE: examples/integration/contentful/pages/tags/_tag.vue ================================================ ================================================ FILE: examples/integration/contentful/plugins/README.md ================================================ # PLUGINS This directory contains your Javascript plugins that you want to run before instantiating the root vue.js application. More information about the usage of this directory in the documentation: https://nuxtjs.org/guide/plugins **This directory is not required, you can delete it if you don't want to use it.** ================================================ FILE: examples/integration/contentful/plugins/contentful.js ================================================ const contentful = require('contentful') const defaultConfig = { CTF_SPACE_ID: process.env.CTF_SPACE_ID, CTF_CDA_ACCESS_TOKEN: process.env.CTF_CDA_ACCESS_TOKEN } module.exports = { createClient (config = defaultConfig) { return contentful.createClient({ space: config.CTF_SPACE_ID, accessToken: config.CTF_CDA_ACCESS_TOKEN }) } } ================================================ FILE: examples/integration/contentful/plugins/i18n.js ================================================ import Vue from 'vue' import VueI18n from 'vue-i18n' const DEFAULT_LOCALE = 'en-US' Vue.use(VueI18n) export default ({ app, req }) => { let locale = DEFAULT_LOCALE if (process.client) { const navigator = window.navigator const languages = navigator.languages || navigator.language || navigator.browserLanguage || navigator.userLanguage locale = languages[0] } else if (req) { locale = req.headers['accept-language'].split(',')[0] } console.log(`plugins: locale=${locale}`) app.i18n = new VueI18n({ locale, fallbackLocale: DEFAULT_LOCALE, messages: { 'en-US': require('~/locales/en-US.json'), 'ja': require('~/locales/ja.json') }, dateTimeFormats: { 'en-US': { short: { year: 'numeric', month: 'short', day: 'numeric' } }, 'ja': { short: { year: 'numeric', month: 'short', day: 'numeric' } } } }) } ================================================ FILE: examples/integration/contentful/static/README.md ================================================ # STATIC This directory contains your static files. Each file inside this directory is mapped to /. Example: /static/robots.txt is mapped as /robots.txt. More information about the usage of this directory in the documentation: https://nuxtjs.org/guide/assets#static **This directory is not required, you can delete it if you don't want to use it.** ================================================ FILE: examples/integration/contentful/store/README.md ================================================ # STORE This directory contains your Vuex Store files. Vuex Store option is implemented in the Nuxt.js framework. Creating a index.js file in this directory activate the option in the framework automatically. More information about the usage of this directory in the documentation: https://nuxtjs.org/guide/vuex-store **This directory is not required, you can delete it if you don't want to use it.** ================================================ FILE: examples/interpolation/places/index.html ================================================ component interpolation
================================================ FILE: examples/interpolation/slots/index.html ================================================ component interpolation
================================================ FILE: examples/module/nuxt/.gitignore ================================================ # Created by .ignore support plugin (hsz.mobi) ### Node template # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # TypeScript v1 declaration files typings/ # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env # parcel-bundler cache (https://parceljs.org/) .cache # next.js build output .next # nuxt.js build output .nuxt # Nuxt generate dist # vuepress build output .vuepress/dist # Serverless directories .serverless # IDE / Editor .idea .editorconfig # Service worker sw.* # Mac OSX .DS_Store ================================================ FILE: examples/module/nuxt/README.md ================================================ # vue-i18n-extensions-module-using-example-for-nuxt > vue-i18n-extentions module using example for Nuxt.js ## Build Setup ``` bash # install dependencies $ npm install # serve with hot reload at localhost:3000 $ npm run dev # build for production and launch server $ npm run build $ npm start # generate static project $ npm run generate ``` For detailed explanation on how things work, checkout [Nuxt.js docs](https://nuxtjs.org). ================================================ FILE: examples/module/nuxt/assets/README.md ================================================ # ASSETS **This directory is not required, you can delete it if you don't want to use it.** This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked). ================================================ FILE: examples/module/nuxt/components/Logo.vue ================================================ ================================================ FILE: examples/module/nuxt/components/README.md ================================================ # COMPONENTS **This directory is not required, you can delete it if you don't want to use it.** The components directory contains your Vue.js Components. _Nuxt.js doesn't supercharge these components._ ================================================ FILE: examples/module/nuxt/layouts/README.md ================================================ # LAYOUTS **This directory is not required, you can delete it if you don't want to use it.** This directory contains your Application Layouts. More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts). ================================================ FILE: examples/module/nuxt/layouts/default.vue ================================================ ================================================ FILE: examples/module/nuxt/locales/en.json ================================================ { "title": { "main": "nuxt", "sub": "Nuxt.js project" }, "links": { "docs": "Documentation", "github": "GitHub" } } ================================================ FILE: examples/module/nuxt/locales/ja.json ================================================ { "title": { "main": "nuxt", "sub": "Nuxt.js プロジェクト" }, "links": { "docs": "ドキュメント", "github": "GitHub" } } ================================================ FILE: examples/module/nuxt/middleware/README.md ================================================ # MIDDLEWARE **This directory is not required, you can delete it if you don't want to use it.** This directory contains your application middleware. Middleware let you define custom functions that can be run before rendering either a page or a group of pages. More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). ================================================ FILE: examples/module/nuxt/nuxt.config.js ================================================ const Vue = require('vue') const VueI18n = require('vue-i18n') const i18nExtensions = require('vue-i18n-extensions') Vue.use(VueI18n) const i18n = new VueI18n({ locale: 'en', messages: { en: require('./locales/en.json'), ja: require('./locales/ja.json') } }) export default { mode: 'universal', /* ** Headers of the page */ head: { title: process.env.npm_package_name || '', meta: [ { charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { hid: 'description', name: 'description', content: process.env.npm_package_description || '' } ], link: [ { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' } ] }, /* ** Customize the progress-bar color */ loading: { color: '#fff' }, /* ** Global CSS */ css: [ ], /* ** Plugins to load before mounting the App */ plugins: [ ], /* ** Nuxt.js modules */ modules: [ ], /* ** Build configuration */ build: { /* ** You can extend webpack config here */ extend (config, ctx) { // inject `compilerOptions` to vue-loader options config.module.rules.forEach(rule => { if (rule.loader === 'vue-loader') { rule.options.compilerOptions = rule.options.compilerOptions || {} rule.options.compilerOptions.modules = [i18nExtensions.module(i18n)] } }) } } } ================================================ FILE: examples/module/nuxt/package.json ================================================ { "name": "vue-i18n-extensions-module-using-example-for-nuxt", "version": "1.0.0", "private": true, "scripts": { "dev": "nuxt", "build": "nuxt build", "start": "nuxt start", "generate": "nuxt generate" }, "dependencies": { "nuxt": "^2.0.0", "vue-i18n": "^8.11.2" }, "devDependencies": { "nodemon": "^1.18.9", "vue-i18n-extensions": "^0.2.1" } } ================================================ FILE: examples/module/nuxt/pages/README.md ================================================ # PAGES This directory contains your Application Views and Routes. The framework reads all the `*.vue` files inside this directory and creates the router of your application. More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing). ================================================ FILE: examples/module/nuxt/pages/index.vue ================================================ ================================================ FILE: examples/module/nuxt/plugins/README.md ================================================ # PLUGINS **This directory is not required, you can delete it if you don't want to use it.** This directory contains Javascript plugins that you want to run before mounting the root Vue.js application. More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins). ================================================ FILE: examples/module/nuxt/static/README.md ================================================ # STATIC **This directory is not required, you can delete it if you don't want to use it.** This directory contains your static files. Each file inside this directory is mapped to `/`. Thus you'd want to delete this README.md before deploying to production. Example: `/static/robots.txt` is mapped as `/robots.txt`. More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static). ================================================ FILE: examples/module/nuxt/store/README.md ================================================ # STORE **This directory is not required, you can delete it if you don't want to use it.** This directory contains your Vuex Store files. Vuex Store option is implemented in the Nuxt.js framework. Creating a file in this directory automatically activates the option in the framework. More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). ================================================ FILE: examples/module/rollup/.gitignore ================================================ .DS_Store node_modules/ dist/bundle.css dist/bundle.js npm-debug.log yarn-error.log # Editor directories and files .idea *.suo *.ntvs* *.njsproj *.sln ================================================ FILE: examples/module/rollup/README.md ================================================ # vue-i18n-extensions-module-using-example-for-rollup > vue-i18n-extentions module using example for rollup ## Build Setup ``` bash # install dependencies npm install # build for production npm run build # serve at localhost:5000 npm run serve ``` For detailed explanation on how things work, consult the [docs for rollup-plugin-vue](http://vuejs.github.io/rollup-plugin-vue/) ================================================ FILE: examples/module/rollup/dist/index.html ================================================ vue-i18n-extensions-module-using-example-for-rollup
================================================ FILE: examples/module/rollup/package.json ================================================ { "name": "vue-i18n-extensions-module-using-example-for-rollup", "description": "vue-i18n-extensions module using example for rollup", "version": "1.0.0", "dependencies": { "vue": "^2.6.10" }, "devDependencies": { "cross-env": "^5.2.0", "rollup": "^1.14.6", "rollup-plugin-buble": "^0.19.6", "rollup-plugin-commonjs": "^10.0.0", "rollup-plugin-node-resolve": "^5.0.1", "rollup-plugin-replace": "^2.2.0", "rollup-plugin-serve": "^1.0.2", "rollup-plugin-uglify": "^6.0.2", "rollup-plugin-vue": "^5.0.0", "vue-i18n": "^8.11.2", "vue-i18n-extensions": "^0.2.1", "vue-template-compiler": "^2.6.10" }, "license": "MIT", "private": true, "scripts": { "build": "cross-env NODE_ENV=production rollup -c ./rollup.config.js --format iife", "serve": "cross-env NODE_ENV=development rollup -c ./rollup.config.js --format iife" } } ================================================ FILE: examples/module/rollup/rollup.config.js ================================================ import vue from 'rollup-plugin-vue' import buble from 'rollup-plugin-buble' import resolve from 'rollup-plugin-node-resolve' import replace from 'rollup-plugin-replace' import commonjs from 'rollup-plugin-commonjs' import { uglify } from 'rollup-plugin-uglify' import serve from 'rollup-plugin-serve' import Vue from 'vue' import VueI18n from 'vue-i18n' import { module } from 'vue-i18n-extensions' Vue.use(VueI18n) const i18n = new VueI18n({ locale: 'en', messages: { en: { headers: { links: 'Essential Links', ecosystem: 'Ecosystem' }, items: { docs: 'Core Docs', forum: 'Forum', chat: 'Gitter Chat', twitter: 'Twitter', routing: 'vue-router', store: 'vuex', webpack: 'vue-loader', curated: 'awesome-vue' } }, ja: { } } }) const external = [ //'vue' ] const globals = { //vue: 'Vue' } const plugins = [ resolve({ customResolveOptions: { moduleDirectory: 'node_modules' } }), replace({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) }), commonjs(), vue({ css: true, compileTemplate: true, template: { compilerOptions: { modules: [module(i18n)] } } }), buble({ exclude: 'node_modules/**' }) ] if (process.env.NODE_ENV === 'production') { plugins.push(uglify()) } else if (process.env.NODE_ENV === 'development') { plugins.push(serve({ contentBase: ['dist'], port: 5000, open: true })) } export default { input: 'src/index.js', external, output: { file: 'dist/bundle.js', name: 'app', format: 'iife', sourcemap: true, globals }, plugins } ================================================ FILE: examples/module/rollup/src/App.vue ================================================ ================================================ FILE: examples/module/rollup/src/index.js ================================================ import Vue from 'vue' import App from './App.vue' new Vue({ el: '#app', render: h => h(App) }) ================================================ FILE: examples/module/webpack/.gitignore ================================================ .DS_Store node_modules /dist # local env files .env.local .env.*.local # Log files npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.sw? ================================================ FILE: examples/module/webpack/README.md ================================================ # vue-i18n-extensions-module-using-example-for-webpack > vue-i18n-extentions module using example for webpack ## Project setup ``` npm install ``` ### Compiles and hot-reloads for development ``` npm run serve ``` ### Compiles and minifies for production ``` npm run build ``` ### Customize configuration See [Configuration Reference](https://cli.vuejs.org/config/). ================================================ FILE: examples/module/webpack/babel.config.js ================================================ module.exports = { presets: [ '@vue/app' ] } ================================================ FILE: examples/module/webpack/package.json ================================================ { "name": "vue-i18n-extensions-module-using-example-for-webpack", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build" }, "dependencies": { "core-js": "^2.6.5", "vue": "^2.6.10", "vue-i18n": "^8.11.2" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.8.0", "@vue/cli-service": "^3.8.0", "vue-i18n-extensions": "^0.2.1", "vue-template-compiler": "^2.6.10" }, "postcss": { "plugins": { "autoprefixer": {} } }, "browserslist": [ "> 1%", "last 2 versions" ] } ================================================ FILE: examples/module/webpack/public/index.html ================================================ webpack
================================================ FILE: examples/module/webpack/src/App.vue ================================================ ================================================ FILE: examples/module/webpack/src/main.js ================================================ import Vue from 'vue' import App from './App.vue' new Vue({ el: '#app', render: h => h(App) }) ================================================ FILE: examples/module/webpack/vue.config.js ================================================ const Vue = require('vue') var VueI18n = require('vue-i18n') const i18nExtensions = require('vue-i18n-extensions') Vue.use(VueI18n) const i18n = new VueI18n({ locale: 'ja', messages: { en: { headers: { links: 'Essential Links', ecosystem: 'Ecosystem' }, items: { docs: 'Core Docs', forum: 'Forum', chat: 'Gitter Chat', twitter: 'Twitter', routing: 'vue-router', store: 'vuex', webpack: 'vue-loader', curated: 'awesome-vue' } }, ja: { } }, missing: function (locale, key) { console.warn('missing:', locale, key) } }) module.exports = { chainWebpack: config => { config.module .rule('vue') .use('vue-loader') .loader('vue-loader') .tap(options => { options.compilerOptions.modules = [i18nExtensions.module(i18n)] return options }) } } ================================================ FILE: examples/number/index.html ================================================ number localization

{{ $t('money') }}: {{ $n(money, 'currency') }}

================================================ FILE: examples/number-formatting/index.html ================================================ number custom formatting

{{ $t('money') }}:

{{ props.fraction }}
================================================ FILE: examples/perf/.gitignore ================================================ .DS_Store node_modules/ dist/ generate/*.json generate/*.png npm-debug.log yarn-error.log # Editor directories and files .idea *.suo *.ntvs* *.njsproj *.sln ================================================ FILE: examples/perf/README.md ================================================ # vue-i18n-performance-comparison-example > vue-i18n performance comparison example ## Performance measurement prepare ```sh # setup performance comparison examples $ npm run setup # build performance comparison examples $ npm run build ``` ## Performance measurement ```sh # serve perform comparison examples $ npm run serve # measure performance !! $ npm run perform ``` ## Performance measurement targets - Plain: Render TODO items only (No translation) - Method: Render TODO items with `$t` method - Directive: Render TODO items with `v-t` custom directive - Compile: Render TODO items with compiler module ## Measurement enviroments - Vue.config.performance = true - Builded Vue CLI v3 - Development build (Disable Production build) - Headless chrome (pappeteer) ## An approach to measurement Render 1000 TODO items, after that remove 10 TODO items. ## Measurement items - Re-render user timing pass time, when remove TODO item. ================================================ FILE: examples/perf/compiler/.gitignore ================================================ .DS_Store node_modules /dist # local env files .env.local .env.*.local # Log files npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.sw? ================================================ FILE: examples/perf/compiler/README.md ================================================ # compiler > List rendering with vue-i18n compiler module pre-localization ================================================ FILE: examples/perf/compiler/babel.config.js ================================================ module.exports = { presets: [ '@vue/app' ] } ================================================ FILE: examples/perf/compiler/package.json ================================================ { "name": "vue-i18n-with-compiler", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build" }, "dependencies": { "core-js": "^2.6.5", "vue": "^2.6.10", "vue-i18n": "^8.11.2" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.8.0", "@vue/cli-service": "^3.8.0", "vue-i18n-extensions": "^0.2.1", "vue-template-compiler": "^2.6.10" }, "postcss": { "plugins": { "autoprefixer": {} } }, "browserslist": [ "> 1%", "last 2 versions" ] } ================================================ FILE: examples/perf/compiler/public/index.html ================================================ compiler
================================================ FILE: examples/perf/compiler/src/App.vue ================================================ ================================================ FILE: examples/perf/compiler/src/main.js ================================================ import Vue from 'vue' import App from './App.vue' Vue.config.performance = true new Vue({ el: '#app', render: h => h(App) }) ================================================ FILE: examples/perf/compiler/vue.config.js ================================================ const Vue = require('vue') var VueI18n = require('vue-i18n') const i18nExtensions = require('vue-i18n-extensions') Vue.use(VueI18n) const i18n = new VueI18n({ locale: 'jp', messages: { en: { label: 'x' }, jp: { label: '☓' } } }) module.exports = { publicPath: '/compiler/dist', chainWebpack: config => { config.module .rule('vue') .use('vue-loader') .loader('vue-loader') .tap(options => { options.compilerOptions.modules = [i18nExtensions.module(i18n)] return options }) } } ================================================ FILE: examples/perf/directive/.gitignore ================================================ .DS_Store node_modules /dist # local env files .env.local .env.*.local # Log files npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.sw? ================================================ FILE: examples/perf/directive/README.md ================================================ # vue-i18n-with-directive > List rendering with vue-i18n `v-t` custom directive ================================================ FILE: examples/perf/directive/babel.config.js ================================================ module.exports = { presets: [ '@vue/app' ] } ================================================ FILE: examples/perf/directive/package.json ================================================ { "name": "vue-i18n-with-directive", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build" }, "dependencies": { "core-js": "^2.6.5", "vue": "^2.6.10", "vue-i18n": "^8.11.2" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.8.0", "@vue/cli-service": "^3.8.0", "vue-template-compiler": "^2.6.10" }, "postcss": { "plugins": { "autoprefixer": {} } }, "browserslist": [ "> 1%", "last 2 versions" ] } ================================================ FILE: examples/perf/directive/public/index.html ================================================ directive
================================================ FILE: examples/perf/directive/src/App.vue ================================================ ================================================ FILE: examples/perf/directive/src/main.js ================================================ import Vue from 'vue' import VueI18n from 'vue-i18n' import App from './App.vue' Vue.config.performance = true Vue.use(VueI18n) const i18n = new VueI18n({ locale: 'jp', messages: { en: { label: 'x' }, jp: { label: '☓' } } }) new Vue({ el: '#app', i18n, render: h => h(App) }) ================================================ FILE: examples/perf/directive/vue.config.js ================================================ module.exports = { publicPath: '/directive/dist' } ================================================ FILE: examples/perf/generate/.gitkeep ================================================ ================================================ FILE: examples/perf/index.js ================================================ const puppeteer = require('puppeteer') function parseTimings (timings) { const durationPairs = [] let pair = null timings.forEach(timing => { if (timing.ph === 'b') { pair = { begin: timing } } else if (timing.ph === 'e') { if (pair && pair.begin) { pair.end = timing durationPairs.push(pair) pair = null } } }) return durationPairs } async function perform (browser, target, url, profileName) { const page = await browser.newPage() try { await page.tracing.start({ path: `./generate/${profileName}.json` }) await page.goto(url) for (let i = 0; i < 10; i++) { await page.waitForSelector('#app button') await page.click('#app button') } await page.tracing.stop() await page.screenshot({ path: `./generate/${profileName}.png` }); const profile = require(`./generate/${profileName}.json`) const traces = profile.traceEvents const timings = traces.filter(timing => { return timing.cat.match(/blink\.user_timing/) && timing.name.match(/.* render$/) }) const durations = parseTimings(timings) let sum = 0.0 durations.forEach((duration, index) => { const time = (duration.end.ts - duration.begin.ts) / 1000 sum += time console.log(`${target} ${index + 1}: ${time} ms`) }) console.log(`${target} average: ${sum / 10} ms`) } catch (e) { console.error(e) } finally { await page.close() } } async function main () { const browser = await puppeteer.launch() try { await perform(browser, 'plain', 'http://localhost:5000/plain/dist/', 'plain') await perform(browser, 'method', 'http://localhost:5000/method/dist/', 'method') await perform(browser, 'directive', 'http://localhost:5000/directive/dist/', 'directive') await perform(browser, 'compiler', 'http://localhost:5000/compiler/dist/', 'compiler') } catch (e) { console.error(e) } finally { browser.close() } } main() ================================================ FILE: examples/perf/method/.gitignore ================================================ .DS_Store node_modules /dist # local env files .env.local .env.*.local # Log files npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.sw? ================================================ FILE: examples/perf/method/README.md ================================================ # vue-i18n-with-method > List rendering with vue-i18n `$t` method ================================================ FILE: examples/perf/method/babel.config.js ================================================ module.exports = { presets: [ '@vue/app' ] } ================================================ FILE: examples/perf/method/package.json ================================================ { "name": "vue-i18n-with-method", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build" }, "dependencies": { "core-js": "^2.6.5", "vue": "^2.6.10", "vue-i18n": "^8.11.2" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.8.0", "@vue/cli-service": "^3.8.0", "vue-template-compiler": "^2.6.10" }, "postcss": { "plugins": { "autoprefixer": {} } }, "browserslist": [ "> 1%", "last 2 versions" ] } ================================================ FILE: examples/perf/method/public/index.html ================================================ method
================================================ FILE: examples/perf/method/src/App.vue ================================================ ================================================ FILE: examples/perf/method/src/main.js ================================================ import Vue from 'vue' import VueI18n from 'vue-i18n' import App from './App.vue' Vue.config.performance = true Vue.use(VueI18n) const i18n = new VueI18n({ locale: 'jp', messages: { en: { label: 'x' }, jp: { label: '☓' } } }) new Vue({ el: '#app', i18n, render: h => h(App) }) ================================================ FILE: examples/perf/method/vue.config.js ================================================ module.exports = { publicPath: '/method/dist' } ================================================ FILE: examples/perf/package.json ================================================ { "name": "vue-i18n-performance-comparison-example", "description": "vue-i18n performance comparison example", "version": "1.0.0", "main": "./index.js", "private": true, "scripts": { "setup": "cd plain && npm i && cd ../method && npm i && cd ../directive && npm i && cd ../compiler && npm i && cd ../", "setup:clean": "cd plain && rm -rf ./node_modules ./package-lock.json && cd ../method && rm -rf ./node_modules ./package-lock.json && cd ../directive && rm -rf ./node_modules ./package-lock.json && cd ../compiler && rm -rf ./node_modules ./package-lock.json && cd ../", "build": "cd plain && npm run build && cd ../method && npm run build && cd ../directive && npm run build && cd ../compiler && npm run build && cd ../", "perform": "node ./index.js", "serve": "serve ./", "clean": "rm -rf ./generate/*.json ./generate/*.png" }, "author": "kazuya kawaguchi ", "license": "MIT", "dependencies": { "puppeteer": "^1.17.0", "serve": "^11.0.1" } } ================================================ FILE: examples/perf/plain/.gitignore ================================================ .DS_Store node_modules /dist # local env files .env.local .env.*.local # Log files npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.sw? ================================================ FILE: examples/perf/plain/README.md ================================================ # list-perform-comparison-for-vue-i18n > Plain list rendering (due to comparison) ================================================ FILE: examples/perf/plain/babel.config.js ================================================ module.exports = { presets: [ '@vue/app' ] } ================================================ FILE: examples/perf/plain/package.json ================================================ { "name": "list-perform-comparison-for-vue-i18n", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build" }, "dependencies": { "core-js": "^2.6.5", "vue": "^2.6.10" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.8.0", "@vue/cli-service": "^3.8.0", "vue-template-compiler": "^2.6.10" }, "postcss": { "plugins": { "autoprefixer": {} } }, "browserslist": [ "> 1%", "last 2 versions" ] } ================================================ FILE: examples/perf/plain/public/index.html ================================================ plain
================================================ FILE: examples/perf/plain/src/App.vue ================================================ ================================================ FILE: examples/perf/plain/src/main.js ================================================ import Vue from 'vue' import App from './App.vue' Vue.config.performance = true new Vue({ el: '#app', render: h => h(App) }) ================================================ FILE: examples/perf/plain/vue.config.js ================================================ module.exports = { publicPath: '/plain/dist' } ================================================ FILE: examples/sfc/.gitignore ================================================ .DS_Store node_modules /dist # local env files .env.local .env.*.local # Log files npm-debug.log* yarn-debug.log* yarn-error.log* # Editor directories and files .idea .vscode *.suo *.ntvs* *.njsproj *.sln *.sw? ================================================ FILE: examples/sfc/README.md ================================================ # sfc > vue-i18n custom blocks example ## Project setup ``` npm install ``` ### Compiles and hot-reloads for development ``` npm run serve ``` ================================================ FILE: examples/sfc/babel.config.js ================================================ module.exports = { presets: [ '@vue/app' ] } ================================================ FILE: examples/sfc/package.json ================================================ { "name": "sfc", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build" }, "dependencies": { "core-js": "^2.6.5", "vue": "^2.6.10", "vue-i18n": "^8.11.2" }, "devDependencies": { "@kazupon/vue-i18n-loader": "^0.3.0", "@vue/cli-plugin-babel": "^3.8.0", "@vue/cli-service": "^3.8.0", "vue-template-compiler": "^2.6.10" }, "postcss": { "plugins": { "autoprefixer": {} } }, "browserslist": [ "> 1%", "last 2 versions" ] } ================================================ FILE: examples/sfc/public/index.html ================================================ sfc
================================================ FILE: examples/sfc/src/App.vue ================================================ { "en": { "hello": "hello world!" }, "ja": { "hello": "こんにちは、世界!" } } ================================================ FILE: examples/sfc/src/main.js ================================================ import Vue from 'vue' import App from './App.vue' import VueI18n from 'vue-i18n' Vue.config.productionTip = false Vue.use(VueI18n) const i18n = new VueI18n({ locale: 'en', messages: { en: {} } }) new Vue({ i18n, render: h => h(App) }).$mount('#app') ================================================ FILE: examples/sfc/vue.config.js ================================================ module.exports = { chainWebpack: config => { config.module .rule('i18n') .resourceQuery(/blockType=i18n/) .type('javascript/auto') .use('i18n') .loader('@kazupon/vue-i18n-loader') .end() } } ================================================ FILE: examples/ssr/hn2/.babelrc ================================================ { "presets": [ ["env", { "modules": false }] ], "plugins": [ "syntax-dynamic-import" ] } ================================================ FILE: examples/ssr/hn2/.gitignore ================================================ .DS_Store node_modules/ dist/ npm-debug.log .idea *.iml ================================================ FILE: examples/ssr/hn2/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2013-present, Yuxi (Evan) You Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: examples/ssr/hn2/README.md ================================================ # vue-i18n for Server-Side Rendering > This examples is based on vue-hackernews-2.0 ## Build Setup **Requires Node.js 7+** ``` bash # install dependencies npm install # or yarn # serve in dev mode, with hot reload at localhost:8080 npm run dev # build for production npm run build # serve in production mode npm start ``` ## License MIT ================================================ FILE: examples/ssr/hn2/build/setup-dev-server.js ================================================ const path = require('path') const webpack = require('webpack') const MFS = require('memory-fs') const clientConfig = require('./webpack.client.config') const serverConfig = require('./webpack.server.config') const readFile = (fs, file) => { try { return fs.readFileSync(path.join(clientConfig.output.path, file), 'utf-8') } catch (e) {} } module.exports = function setupDevServer (app, cb) { let bundle, clientManifest let resolve const readyPromise = new Promise(r => { resolve = r }) const ready = (...args) => { resolve() cb(...args) } // modify client config to work with hot middleware clientConfig.entry.app = ['webpack-hot-middleware/client', clientConfig.entry.app] clientConfig.output.filename = '[name].js' clientConfig.plugins.push( new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin() ) // dev middleware const clientCompiler = webpack(clientConfig) const devMiddleware = require('webpack-dev-middleware')(clientCompiler, { publicPath: clientConfig.output.publicPath, noInfo: true }) app.use(devMiddleware) clientCompiler.plugin('done', stats => { stats = stats.toJson() stats.errors.forEach(err => console.error(err)) stats.warnings.forEach(err => console.warn(err)) if (stats.errors.length) return clientManifest = JSON.parse(readFile( devMiddleware.fileSystem, 'vue-ssr-client-manifest.json' )) if (bundle) { ready(bundle, { clientManifest }) } }) // hot middleware app.use(require('webpack-hot-middleware')(clientCompiler, { heartbeat: 5000 })) // watch and update server renderer const serverCompiler = webpack(serverConfig) const mfs = new MFS() serverCompiler.outputFileSystem = mfs serverCompiler.watch({}, (err, stats) => { if (err) throw err stats = stats.toJson() if (stats.errors.length) return // read bundle generated by vue-ssr-webpack-plugin bundle = JSON.parse(readFile(mfs, 'vue-ssr-server-bundle.json')) if (clientManifest) { ready(bundle, { clientManifest }) } }) return readyPromise } ================================================ FILE: examples/ssr/hn2/build/vue-loader.config.js ================================================ module.exports = { extractCSS: process.env.NODE_ENV === 'production', preserveWhitespace: false, postcss: [ require('autoprefixer')({ browsers: ['last 3 versions'] }) ] } ================================================ FILE: examples/ssr/hn2/build/webpack.base.config.js ================================================ const path = require('path') const webpack = require('webpack') const vueConfig = require('./vue-loader.config') const ExtractTextPlugin = require('extract-text-webpack-plugin') const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') const isProd = process.env.NODE_ENV === 'production' module.exports = { devtool: isProd ? false : '#cheap-module-source-map', output: { path: path.resolve(__dirname, '../dist'), publicPath: '/dist/', filename: '[name].[chunkhash].js' }, resolve: { alias: { 'public': path.resolve(__dirname, '../public') } }, module: { noParse: /es6-promise\.js$/, // avoid webpack shimming process rules: [ { test: /\.vue$/, loader: 'vue-loader', options: vueConfig }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }, { test: /\.(png|jpg|gif|svg)$/, loader: 'url-loader', options: { limit: 10000, name: '[name].[ext]?[hash]' } }, { test: /\.css$/, use: isProd ? ExtractTextPlugin.extract({ use: 'css-loader?minimize', fallback: 'vue-style-loader' }) : ['vue-style-loader', 'css-loader'] } ] }, performance: { maxEntrypointSize: 300000, hints: isProd ? 'warning' : false }, plugins: isProd ? [ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }), new ExtractTextPlugin({ filename: 'common.[chunkhash].css' }) ] : [ new FriendlyErrorsPlugin() ] } ================================================ FILE: examples/ssr/hn2/build/webpack.client.config.js ================================================ const glob = require('glob') const webpack = require('webpack') const merge = require('webpack-merge') const base = require('./webpack.base.config') const SWPrecachePlugin = require('sw-precache-webpack-plugin') const VueSSRClientPlugin = require('vue-server-renderer/client-plugin') const config = merge({}, base, { entry: { app: './src/entry-client.js' }, resolve: { alias: { 'create-api': './create-api-client.js' } }, plugins: [ // strip dev-only code in Vue source new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), 'process.env.VUE_ENV': '"client"' }), // extract vendor chunks for better caching new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: function (module) { // a module is extracted into the vendor chunk if... return ( // it's inside node_modules /node_modules/.test(module.context) && // and not a CSS file (due to extract-text-webpack-plugin limitation) !/\.css$/.test(module.request) ) } }), // extract webpack runtime & manifest to avoid vendor chunk hash changing // on every build. new webpack.optimize.CommonsChunkPlugin({ name: 'manifest' }), new VueSSRClientPlugin() ] }) if (process.env.NODE_ENV === 'production') { config.plugins.push( // auto generate service worker new SWPrecachePlugin({ cacheId: 'vue-hn', filename: 'service-worker.js', minify: true, dontCacheBustUrlsMatching: /./, staticFileGlobsIgnorePatterns: [/\.map$/, /\.json$/], runtimeCaching: [ { urlPattern: '/', handler: 'networkFirst' }, { urlPattern: /\/(top|new|show|ask|jobs)/, handler: 'networkFirst' }, { urlPattern: '/item/:id', handler: 'networkFirst' }, { urlPattern: '/user/:id', handler: 'networkFirst' } ] }) ) } module.exports = config ================================================ FILE: examples/ssr/hn2/build/webpack.server.config.js ================================================ const webpack = require('webpack') const merge = require('webpack-merge') const base = require('./webpack.base.config') const nodeExternals = require('webpack-node-externals') const VueSSRServerPlugin = require('vue-server-renderer/server-plugin') module.exports = merge(base, { target: 'node', devtool: '#source-map', entry: './src/entry-server.js', output: { filename: 'server-bundle.js', libraryTarget: 'commonjs2' }, resolve: { alias: { 'create-api': './create-api-server.js' } }, // https://webpack.js.org/configuration/externals/#externals // https://github.com/liady/webpack-node-externals externals: nodeExternals({ // do not externalize CSS files in case we need to import it from a dep whitelist: /\.css$/ }), plugins: [ new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), 'process.env.VUE_ENV': '"server"' }), new VueSSRServerPlugin() ] }) ================================================ FILE: examples/ssr/hn2/manifest.json ================================================ { "name": "Vue Hackernews 2.0", "short_name": "Vue HN", "icons": [{ "src": "/public/logo-120.png", "sizes": "120x120", "type": "image/png" }, { "src": "/public/logo-144.png", "sizes": "144x144", "type": "image/png" }, { "src": "/public/logo-152.png", "sizes": "152x152", "type": "image/png" }, { "src": "/public/logo-192.png", "sizes": "192x192", "type": "image/png" },{ "src": "/public/logo-256.png", "sizes": "256x256", "type": "image/png" }, { "src": "/public/logo-384.png", "sizes": "384x384", "type": "image/png" }, { "src": "/public/logo-512.png", "sizes": "512x512", "type": "image/png" }], "start_url": "/", "background_color": "#f2f3f5", "display": "standalone", "theme_color": "#f60" } ================================================ FILE: examples/ssr/hn2/package.json ================================================ { "name": "vue-i18n-ssr", "description": "vue-i18n for server-side rendering", "dependencies": { "compression": "^1.6.2", "cross-env": "^4.0.0", "es6-promise": "^4.1.0", "express": "^4.15.2", "extract-text-webpack-plugin": "^2.1.0", "firebase": "^3.7.2", "lru-cache": "^4.0.2", "route-cache": "0.4.2", "serve-favicon": "^2.4.1", "vue": "^2.4.2", "vue-router": "^2.7.0", "vue-server-renderer": "^2.4.1", "vuex": "^2.3.1", "vue-i18n": "^7.2.0", "vuex-router-sync": "^4.1.2" }, "devDependencies": { "autoprefixer": "^6.7.7", "babel-core": "^6.24.1", "babel-loader": "^7.1.1", "babel-preset-env": "^1.4.0", "babel-plugin-syntax-dynamic-import": "^6.18.0", "css-loader": "^0.28.0", "file-loader": "^0.11.1", "friendly-errors-webpack-plugin": "^1.6.1", "glob": "^7.1.1", "rimraf": "^2.6.1", "stylus": "^0.54.5", "stylus-loader": "^3.0.1", "sw-precache-webpack-plugin": "^0.10.1", "url-loader": "^0.5.8", "vue-loader": "^13.0.1", "vue-style-loader": "^3.0.0", "vue-template-compiler": "^2.4.2", "vue-i18n-extensions": "^0.1.0", "webpack": "^3.2.0", "webpack-dev-middleware": "^1.10.1", "webpack-hot-middleware": "^2.17.1", "webpack-merge": "^4.0.0", "webpack-node-externals": "^1.5.4" }, "engines": { "node": ">=7.0", "npm": ">=4.0" }, "private": true, "scripts": { "build": "rimraf dist && npm run build:client && npm run build:server", "build:client": "cross-env NODE_ENV=production webpack --config build/webpack.client.config.js --progress --hide-modules", "build:server": "cross-env NODE_ENV=production webpack --config build/webpack.server.config.js --progress --hide-modules", "dev": "node server", "start": "cross-env NODE_ENV=production node server", "postinstall": "npm link ../../../" } } ================================================ FILE: examples/ssr/hn2/server.js ================================================ const fs = require('fs') const path = require('path') const LRU = require('lru-cache') const express = require('express') const favicon = require('serve-favicon') const compression = require('compression') const microcache = require('route-cache') const resolve = file => path.resolve(__dirname, file) const { createBundleRenderer } = require('vue-server-renderer') const { directive } = require('vue-i18n-extensions') const isProd = process.env.NODE_ENV === 'production' const useMicroCache = process.env.MICRO_CACHE !== 'false' const serverInfo = `express/${require('express/package.json').version} ` + `vue-server-renderer/${require('vue-server-renderer/package.json').version}` const app = express() const template = fs.readFileSync(resolve('./src/index.template.html'), 'utf-8') function createRenderer (bundle, options) { // https://github.com/vuejs/vue/blob/dev/packages/vue-server-renderer/README.md#why-use-bundlerenderer return createBundleRenderer(bundle, Object.assign(options, { template, // for component caching cache: LRU({ max: 1000, maxAge: 1000 * 60 * 15 }), // this is only needed when vue-server-renderer is npm-linked basedir: resolve('./dist'), // recommended for performance runInNewContext: false, directives: { t: directive } })) } let renderer let readyPromise if (isProd) { // In production: create server renderer using built server bundle. // The server bundle is generated by vue-ssr-webpack-plugin. const bundle = require('./dist/vue-ssr-server-bundle.json') // The client manifests are optional, but it allows the renderer // to automatically infer preload/prefetch links and directly add ================================================ FILE: examples/ssr/hn2/src/api/create-api-client.js ================================================ import Firebase from 'firebase/app' import 'firebase/database' export function createAPI ({ config, version }) { Firebase.initializeApp(config) return Firebase.database().ref(version) } ================================================ FILE: examples/ssr/hn2/src/api/create-api-server.js ================================================ import Firebase from 'firebase' import LRU from 'lru-cache' export function createAPI ({ config, version }) { let api // this piece of code may run multiple times in development mode, // so we attach the instantiated API to `process` to avoid duplications if (process.__API__) { api = process.__API__ } else { Firebase.initializeApp(config) api = process.__API__ = Firebase.database().ref(version) api.onServer = true // fetched item cache api.cachedItems = LRU({ max: 1000, maxAge: 1000 * 60 * 15 // 15 min cache }) // cache the latest story ids api.cachedIds = {} ;['top', 'new', 'show', 'ask', 'job'].forEach(type => { api.child(`${type}stories`).on('value', snapshot => { api.cachedIds[type] = snapshot.val() }) }) } return api } ================================================ FILE: examples/ssr/hn2/src/api/index.js ================================================ // this is aliased in webpack config based on server/client build import { createAPI } from 'create-api' const logRequests = !!process.env.DEBUG_API const api = createAPI({ version: '/v0', config: { databaseURL: 'https://hacker-news.firebaseio.com' } }) // warm the front page cache every 15 min // make sure to do this only once across all requests if (api.onServer) { warmCache() } function warmCache () { fetchItems((api.cachedIds.top || []).slice(0, 30)) setTimeout(warmCache, 1000 * 60 * 15) } function fetch (child) { logRequests && console.log(`fetching ${child}...`) const cache = api.cachedItems if (cache && cache.has(child)) { logRequests && console.log(`cache hit for ${child}.`) return Promise.resolve(cache.get(child)) } else { return new Promise((resolve, reject) => { api.child(child).once('value', snapshot => { const val = snapshot.val() // mark the timestamp when this item is cached if (val) val.__lastUpdated = Date.now() cache && cache.set(child, val) logRequests && console.log(`fetched ${child}.`) resolve(val) }, reject) }) } } export function fetchIdsByType (type) { return api.cachedIds && api.cachedIds[type] ? Promise.resolve(api.cachedIds[type]) : fetch(`${type}stories`) } export function fetchItem (id) { return fetch(`item/${id}`) } export function fetchItems (ids) { return Promise.all(ids.map(id => fetchItem(id))) } export function fetchUser (id) { return fetch(`user/${id}`) } export function watchList (type, cb) { let first = true const ref = api.child(`${type}stories`) const handler = snapshot => { if (first) { first = false } else { cb(snapshot.val()) } } ref.on('value', handler) return () => { ref.off('value', handler) } } ================================================ FILE: examples/ssr/hn2/src/app.js ================================================ import Vue from 'vue' import App from './App.vue' import { createStore } from './store' import { createRouter } from './router' import { createI18n } from './i18n' import { sync } from 'vuex-router-sync' import titleMixin from './util/title' import * as filters from './util/filters' // mixin for handling title Vue.mixin(titleMixin) // register global utility filters. Object.keys(filters).forEach(key => { Vue.filter(key, filters[key]) }) // Expose a factory function that creates a fresh set of store, router, // app instances on each call (which is called for each SSR request) export function createApp () { // create store and router instances const store = createStore() const router = createRouter() const i18n = createI18n() // sync the router with the vuex store. // this registers `store.state.route` sync(store, router) // create the app instance. // here we inject the router, store and ssr context to all child components, // making them available everywhere as `this.$router` and `this.$store`. const app = new Vue({ router, store, i18n, render: h => h(App) }) // expose the app, the router and the store. // note we are not mounting the app here, since bootstrapping will be // different depending on whether we are in a browser or on the server. return { app, router, store } } ================================================ FILE: examples/ssr/hn2/src/components/Comment.vue ================================================ ================================================ FILE: examples/ssr/hn2/src/components/Item.vue ================================================ ================================================ FILE: examples/ssr/hn2/src/components/ProgressBar.vue ================================================ ================================================ FILE: examples/ssr/hn2/src/components/Spinner.vue ================================================ ================================================ FILE: examples/ssr/hn2/src/entry-client.js ================================================ import Vue from 'vue' import 'es6-promise/auto' import { createApp } from './app' import ProgressBar from './components/ProgressBar.vue' // global progress bar const bar = Vue.prototype.$bar = new Vue(ProgressBar).$mount() document.body.appendChild(bar.$el) // a global mixin that calls `asyncData` when a route component's params change Vue.mixin({ beforeRouteUpdate (to, from, next) { const { asyncData } = this.$options if (asyncData) { asyncData({ store: this.$store, route: to }).then(next).catch(next) } else { next() } } }) const { app, router, store } = createApp() // prime the store with server-initialized state. // the state is determined during SSR and inlined in the page markup. if (window.__INITIAL_STATE__) { store.replaceState(window.__INITIAL_STATE__) } // wait until router has resolved all async before hooks // and async components... router.onReady(() => { // Add router hook for handling asyncData. // Doing it after initial route is resolved so that we don't double-fetch // the data that we already have. Using router.beforeResolve() so that all // async components are resolved. router.beforeResolve((to, from, next) => { const matched = router.getMatchedComponents(to) const prevMatched = router.getMatchedComponents(from) let diffed = false const activated = matched.filter((c, i) => { return diffed || (diffed = (prevMatched[i] !== c)) }) const asyncDataHooks = activated.map(c => c.asyncData).filter(_ => _) if (!asyncDataHooks.length) { return next() } bar.start() Promise.all(asyncDataHooks.map(hook => hook({ store, route: to }))) .then(() => { bar.finish() next() }) .catch(next) }) // actually mount to DOM app.$mount('#app') }) // service worker if ('https:' === location.protocol && navigator.serviceWorker) { navigator.serviceWorker.register('/service-worker.js') } ================================================ FILE: examples/ssr/hn2/src/entry-server.js ================================================ import { createApp } from './app' const isDev = process.env.NODE_ENV !== 'production' // This exported function will be called by `bundleRenderer`. // This is where we perform data-prefetching to determine the // state of our application before actually rendering it. // Since data fetching is async, this function is expected to // return a Promise that resolves to the app instance. export default context => { return new Promise((resolve, reject) => { const s = isDev && Date.now() const { app, router, store } = createApp() const { url } = context const fullPath = router.resolve(url).route.fullPath if (fullPath !== url) { reject({ url: fullPath }) } // set router's location router.push(url) // wait until router has resolved possible async hooks router.onReady(() => { const matchedComponents = router.getMatchedComponents() // no matched routes if (!matchedComponents.length) { reject({ code: 404 }) } // Call fetchData hooks on components matched by the route. // A preFetch hook dispatches a store action and returns a Promise, // which is resolved when the action is complete and store state has been // updated. Promise.all(matchedComponents.map(({ asyncData }) => asyncData && asyncData({ store, route: router.currentRoute }))).then(() => { isDev && console.log(`data pre-fetch: ${Date.now() - s}ms`) // After all preFetch hooks are resolved, our store is now // filled with the state needed to render the app. // Expose the state on the render context, and let the request handler // inline the state in the HTML response. This allows the client-side // store to pick-up the server-side state without having to duplicate // the initial data fetching on the client. context.state = store.state resolve(app) }).catch(reject) }, reject) }) } ================================================ FILE: examples/ssr/hn2/src/i18n/en-US.json ================================================ { "navigation": { "top": "Top", "new": "New", "show": "Show", "ask": "Ask", "job": "Jobs", "powered": "Built with Vue.js" }, "locales": { "en-US": "EN", "ja-JP": "JP" }, "pagination": { "prev": "prev", "more": "more" }, "time": { "ago": "ago", "units": { "minute": "minute | minutes", "hour": "hour | hours", "day": "day | days" } }, "user": { "id": "User", "created": "Created", "karma": "Karma", "submissions": "submissions", "comments": "comments" }, "item": { "no_comments": "No comments yet.", "comments": "comments" }, "comment": { "reply": "reply | replies", "collapsed": "collapsed" } } ================================================ FILE: examples/ssr/hn2/src/i18n/index.js ================================================ import Vue from 'vue' import VueI18n from 'vue-i18n' import en from './en-US.json' import jp from './ja-JP.json' Vue.use(VueI18n) export function createI18n () { return new VueI18n({ locale: 'en-US', messages: { 'en-US': en, 'ja-JP': jp } }) } ================================================ FILE: examples/ssr/hn2/src/i18n/ja-JP.json ================================================ { "navigation": { "top": "トップ", "new": "新着", "show": "出来事", "ask": "質問", "job": "求人", "powered": "Build with Vue.js" }, "locales": { "en-US": "EN", "ja-JP": "JP" }, "pagination": { "prev": "前", "more": "次" }, "time": { "ago": "前", "units": { "minute": "分", "hour": "時間", "day": "日" } }, "user": { "id": "ユーザ", "created": "作成日", "karma": "カルマ", "submissions": "投稿", "comments": "コメント" }, "item": { "no_comments": "まだコメントはありません。", "comments": "コメント" }, "comment": { "reply": "返信", "collapsed": "を展開する" } } ================================================ FILE: examples/ssr/hn2/src/index.template.html ================================================ {{ title }} ================================================ FILE: examples/ssr/hn2/src/router/index.js ================================================ import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) // route-level code splitting const createListView = id => () => import('../views/CreateListView').then(m => m.default(id)) const ItemView = () => import('../views/ItemView.vue') const UserView = () => import('../views/UserView.vue') export function createRouter () { return new Router({ mode: 'history', scrollBehavior: () => ({ y: 0 }), routes: [ { path: '/top/:page(\\d+)?', component: createListView('top') }, { path: '/new/:page(\\d+)?', component: createListView('new') }, { path: '/show/:page(\\d+)?', component: createListView('show') }, { path: '/ask/:page(\\d+)?', component: createListView('ask') }, { path: '/job/:page(\\d+)?', component: createListView('job') }, { path: '/item/:id(\\d+)', component: ItemView }, { path: '/user/:id', component: UserView }, { path: '/', redirect: '/top' } ] }) } ================================================ FILE: examples/ssr/hn2/src/store/actions.js ================================================ import { fetchUser, fetchItems, fetchIdsByType } from '../api' export default { // ensure data for rendering given list type FETCH_LIST_DATA: ({ commit, dispatch, state }, { type }) => { commit('SET_ACTIVE_TYPE', { type }) return fetchIdsByType(type) .then(ids => commit('SET_LIST', { type, ids })) .then(() => dispatch('ENSURE_ACTIVE_ITEMS')) }, // ensure all active items are fetched ENSURE_ACTIVE_ITEMS: ({ dispatch, getters }) => { return dispatch('FETCH_ITEMS', { ids: getters.activeIds }) }, FETCH_ITEMS: ({ commit, state }, { ids }) => { // on the client, the store itself serves as a cache. // only fetch items that we do not already have, or has expired (3 minutes) const now = Date.now() ids = ids.filter(id => { const item = state.items[id] if (!item) { return true } if (now - item.__lastUpdated > 1000 * 60 * 3) { return true } return false }) if (ids.length) { return fetchItems(ids).then(items => commit('SET_ITEMS', { items })) } else { return Promise.resolve() } }, FETCH_USER: ({ commit, state }, { id }) => { return state.users[id] ? Promise.resolve(state.users[id]) : fetchUser(id).then(user => commit('SET_USER', { id, user })) } } ================================================ FILE: examples/ssr/hn2/src/store/getters.js ================================================ export default { // ids of the items that should be currently displayed based on // current list type and current pagination activeIds (state) { const { activeType, itemsPerPage, lists } = state if (!activeType) { return [] } const page = Number(state.route.params.page) || 1 const start = (page - 1) * itemsPerPage const end = page * itemsPerPage return lists[activeType].slice(start, end) }, // items that should be currently displayed. // this Array may not be fully fetched. activeItems (state, getters) { return getters.activeIds.map(id => state.items[id]).filter(_ => _) } } ================================================ FILE: examples/ssr/hn2/src/store/index.js ================================================ import Vue from 'vue' import Vuex from 'vuex' import actions from './actions' import mutations from './mutations' import getters from './getters' Vue.use(Vuex) export function createStore () { return new Vuex.Store({ state: { activeType: null, itemsPerPage: 20, items: {/* [id: number]: Item */}, users: {/* [id: string]: User */}, lists: { top: [/* number */], new: [], show: [], ask: [], job: [] } }, actions, mutations, getters }) } ================================================ FILE: examples/ssr/hn2/src/store/mutations.js ================================================ import Vue from 'vue' export default { SET_ACTIVE_TYPE: (state, { type }) => { state.activeType = type }, SET_LIST: (state, { type, ids }) => { state.lists[type] = ids }, SET_ITEMS: (state, { items }) => { items.forEach(item => { if (item) { Vue.set(state.items, item.id, item) } }) }, SET_USER: (state, { id, user }) => { Vue.set(state.users, id, user || false) /* false means user not found */ } } ================================================ FILE: examples/ssr/hn2/src/util/filters.js ================================================ export function host (url) { const host = url.replace(/^https?:\/\//, '').replace(/\/.*$/, '') const parts = host.split('.').slice(-3) if (parts[0] === 'www') parts.shift() return parts.join('.') } export function timeAgo (time, $tc) { const between = Date.now() / 1000 - Number(time) if (!$tc) { return between } let t if (between < 3600) { t = ~~(between / 60) return `${t} ${$tc('time.units.minute', t)}` } else if (between < 86400) { t = ~~(between / 3600) return `${t} ${$tc('time.units.hour', t)}` } else { t = ~~(between / 86400) return `${t} ${$tc('time.units.day', t)}` } } ================================================ FILE: examples/ssr/hn2/src/util/title.js ================================================ function getTitle (vm) { const { title } = vm.$options if (title) { return typeof title === 'function' ? title.call(vm) : title } } const serverTitleMixin = { created () { const title = getTitle(this) if (title) { this.$ssrContext.title = `Vue HN 2.0 | ${title}` } } } const clientTitleMixin = { mounted () { const title = getTitle(this) if (title) { document.title = `Vue HN 2.0 | ${title}` } } } export default process.env.VUE_ENV === 'server' ? serverTitleMixin : clientTitleMixin ================================================ FILE: examples/ssr/hn2/src/util.js ================================================ export function timeAgo (time) { const between = Date.now() / 1000 - Number(time) if (between < 3600) { return { value: ~~(between / 60), unit: 'minute' } } else if (between < 86400) { return { value: ~~(between / 3600), unit: 'hour' } } else { return { value: ~~(between / 86400), unit: 'day' } } } ================================================ FILE: examples/ssr/hn2/src/views/CreateListView.js ================================================ import ItemList from './ItemList.vue' const camelize = str => str.charAt(0).toUpperCase() + str.slice(1) // This is a factory function for dynamically creating root-level list views, // since they share most of the logic except for the type of items to display. // They are essentially higher order components wrapping ItemList.vue. export default function createListView (type) { return { name: `${type}-stories-view`, asyncData ({ store }) { return store.dispatch('FETCH_LIST_DATA', { type }) }, title: camelize(type), render (h) { return h(ItemList, { props: { type }}) } } } ================================================ FILE: examples/ssr/hn2/src/views/ItemList.vue ================================================ ================================================ FILE: examples/ssr/hn2/src/views/ItemView.vue ================================================ ================================================ FILE: examples/ssr/hn2/src/views/UserView.vue ================================================ ================================================ FILE: examples/ssr/nuxt/.editorconfig ================================================ # editorconfig.org root = true [*] indent_size = 2 indent_style = space end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false ================================================ FILE: examples/ssr/nuxt/.eslintrc.js ================================================ module.exports = { root: true, parser: 'babel-eslint', env: { browser: true, node: true }, extends: 'standard', // required to lint *.vue files plugins: [ 'html' ], // add your custom rules here rules: {}, globals: {} } ================================================ FILE: examples/ssr/nuxt/.gitignore ================================================ # dependencies node_modules # logs npm-debug.log # Nuxt build .nuxt # Nuxt generate dist ================================================ FILE: examples/ssr/nuxt/README.md ================================================ # ssr-for-nuxt > Server-Side Rendering for Nuxt.js ## Build Setup ``` bash # install dependencies $ npm install # Or yarn install # serve with hot reload at localhost:3000 $ npm run dev # build for production and launch server $ npm run build $ npm start # generate static project $ npm run generate ``` For detailed explanation on how things work, checkout the [Nuxt.js docs](https://github.com/nuxt/nuxt.js). ================================================ FILE: examples/ssr/nuxt/assets/README.md ================================================ # ASSETS This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. More information about the usage of this directory in the documentation: https://nuxtjs.org/guide/assets#webpacked **This directory is not required, you can delete it if you don't want to use it.** ================================================ FILE: examples/ssr/nuxt/components/Logo.vue ================================================ ================================================ FILE: examples/ssr/nuxt/components/README.md ================================================ # COMPONENTS The components directory contains your Vue.js Components. Nuxt.js doesn't supercharge these components. **This directory is not required, you can delete it if you don't want to use it.** ================================================ FILE: examples/ssr/nuxt/layouts/README.md ================================================ # LAYOUTS This directory contains your Application Layouts. More information about the usage of this directory in the documentation: https://nuxtjs.org/guide/views#layouts **This directory is not required, you can delete it if you don't want to use it.** ================================================ FILE: examples/ssr/nuxt/layouts/default.vue ================================================ ================================================ FILE: examples/ssr/nuxt/locales/en.json ================================================ { "title": { "main": "ssr-for-nuxt", "sub": "Server-Side Rendering for Nuxt.js" }, "links": { "docs": "Documentation", "github": "GitHub" } } ================================================ FILE: examples/ssr/nuxt/locales/ja.json ================================================ { "title": { "main": "ssr-for-nuxt", "sub": "サーバサイドレンダリング Nuxt.js プロジェクト" }, "links": { "docs": "ドキュメント", "github": "GitHub" } } ================================================ FILE: examples/ssr/nuxt/middleware/README.md ================================================ # MIDDLEWARE This directory contains your Application Middleware. The middleware lets you define custom function to be ran before rendering a page or a group of pages (layouts). More information about the usage of this directory in the documentation: https://nuxtjs.org/guide/routing#middleware **This directory is not required, you can delete it if you don't want to use it.** ================================================ FILE: examples/ssr/nuxt/nuxt.config.js ================================================ const i18nExtensions = require('vue-i18n-extensions') module.exports = { /* ** Headers of the page */ head: { title: 'ssr-for-nuxt', meta: [ { charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { hid: 'description', name: 'description', content: 'Server-Side Rendering for Nuxt.js' } ], link: [ { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' } ] }, /* ** Customize the progress bar color */ loading: { color: '#3B8070' }, /* ** Build configuration */ build: { vendor: ['vue-i18n'] }, render: { // confiture `render` // see Nuxt.js docs: https://nuxtjs.org/api/configuration-render#bundleRenderer bundleRenderer: { directives: { t: i18nExtensions.directive } } }, plugins: ['~/plugins/i18n.js'] } ================================================ FILE: examples/ssr/nuxt/package.json ================================================ { "name": "ssr-for-nuxt", "version": "1.0.0", "description": "Server-Side Rendering for Nuxt.js", "author": "kazuya kawaguchi ", "private": true, "scripts": { "dev": "nuxt", "build": "nuxt build", "start": "nuxt start", "generate": "nuxt generate", "lint": "eslint --ext .js,.vue --ignore-path .gitignore .", "precommit": "npm run lint", "postinstall": "npm link ../../../" }, "dependencies": { "nuxt": "^1.4.0" }, "devDependencies": { "babel-eslint": "^7.2.3", "eslint": "^4.3.0", "eslint-config-standard": "^10.2.1", "eslint-loader": "^1.9.0", "eslint-plugin-html": "^3.1.1", "eslint-plugin-import": "^2.7.0", "eslint-plugin-node": "^5.1.1", "eslint-plugin-promise": "^3.5.0", "eslint-plugin-standard": "^3.0.1", "vue-i18n-extensions": "^0.1.0" } } ================================================ FILE: examples/ssr/nuxt/pages/README.md ================================================ # PAGES This directory contains your Application Views and Routes. The framework reads all the .vue files inside this directory and create the router of your application. More information about the usage of this directory in the documentation: https://nuxtjs.org/guide/routing ================================================ FILE: examples/ssr/nuxt/pages/index.vue ================================================ ================================================ FILE: examples/ssr/nuxt/plugins/README.md ================================================ # PLUGINS This directory contains your Javascript plugins that you want to run before instantiating the root vue.js application. More information about the usage of this directory in the documentation: https://nuxtjs.org/guide/plugins **This directory is not required, you can delete it if you don't want to use it.** ================================================ FILE: examples/ssr/nuxt/plugins/i18n.js ================================================ import Vue from 'vue' import VueI18n from 'vue-i18n' Vue.use(VueI18n) export default ({ app, store }) => { app.i18n = new VueI18n({ locale: 'ja', messages: { 'en': require('~/locales/en.json'), 'ja': require('~/locales/ja.json') } }) } ================================================ FILE: examples/ssr/nuxt/static/README.md ================================================ # STATIC This directory contains your static files. Each file inside this directory is mapped to /. Example: /static/robots.txt is mapped as /robots.txt. More information about the usage of this directory in the documentation: https://nuxtjs.org/guide/assets#static **This directory is not required, you can delete it if you don't want to use it.** ================================================ FILE: examples/ssr/nuxt/store/README.md ================================================ # STORE This directory contains your Vuex Store files. Vuex Store option is implemented in the Nuxt.js framework. Creating a index.js file in this directory activate the option in the framework automatically. More information about the usage of this directory in the documentation: https://nuxtjs.org/guide/vuex-store **This directory is not required, you can delete it if you don't want to use it.** ================================================ FILE: examples/started/index.html ================================================ getting started

{{ $t("message.hello") }}

================================================ FILE: examples/storybook/.babelrc ================================================ { "presets": [ ["env", { "modules": false }] ] } ================================================ FILE: examples/storybook/.gitignore ================================================ .DS_Store node_modules/ dist/ npm-debug.log yarn-error.log # Editor directories and files .idea *.suo *.ntvs* *.njsproj *.sln ================================================ FILE: examples/storybook/.storybook/addons.js ================================================ /* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */ import '@storybook/addon-actions/register' import '@storybook/addon-links/register' ================================================ FILE: examples/storybook/.storybook/config.js ================================================ import { configure } from '@storybook/vue' import Vue from 'vue' import VueI18n from 'vue-i18n' Vue.use(VueI18n) function loadStories() { require('../src/stories') } configure(loadStories, module) ================================================ FILE: examples/storybook/README.md ================================================ # storybook > A Vue.js project ## Build Setup ``` bash # install dependencies npm install # serve with hot reload at localhost:8080 npm run dev # build for production with minification npm run build ``` For detailed explanation on how things work, consult the [docs for vue-loader](http://vuejs.github.io/vue-loader). ================================================ FILE: examples/storybook/index.html ================================================ storybook
================================================ FILE: examples/storybook/package.json ================================================ { "name": "vue-i18n-storybook ", "description": "vue-i18n example for stroybook", "version": "1.0.0", "author": "kazuya kawaguchi ", "private": true, "scripts": { "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot", "build": "cross-env NODE_ENV=production webpack --progress --hide-modules", "postinstall": "npm link ../../", "storybook": "start-storybook -p 6006", "build-storybook": "build-storybook" }, "dependencies": { "vue": "^2.4.2" }, "devDependencies": { "babel-core": "^6.0.0", "babel-loader": "^6.0.0", "babel-preset-env": "^1.5.1", "cross-env": "^3.0.0", "css-loader": "^0.25.0", "file-loader": "^0.9.0", "vue-loader": "^12.1.0", "vue-template-compiler": "^2.4.2", "webpack": "^2.6.1", "webpack-dev-server": "^3.10.1", "@storybook/vue": "^3.2.3" } } ================================================ FILE: examples/storybook/src/App.vue ================================================ ================================================ FILE: examples/storybook/src/main.js ================================================ import Vue from 'vue' import App from './App.vue' new Vue({ el: '#app', render: h => h(App) }) ================================================ FILE: examples/storybook/src/stories/MyButton.vue ================================================ ================================================ FILE: examples/storybook/src/stories/Welcome.vue ================================================ ================================================ FILE: examples/storybook/src/stories/index.js ================================================ /* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */ import { storiesOf } from '@storybook/vue' import { action } from '@storybook/addon-actions' import { linkTo } from '@storybook/addon-links' import App from '../App.vue' import MyButton from './MyButton.vue' import Welcome from './Welcome.vue' import VueI18n from 'vue-i18n' storiesOf('App', module) .add('basic', () => ({ components: { App }, template: '', i18n: new VueI18n({ locale: 'ja', messages: { en: { message: 'Welcome to Your Vue.js App', links: 'Essential Links', ecosystem: 'EcoSystem' }, ja: { message: 'ようこそ、Vue.js アプリケーションに', links: '必須リンク', ecosystem: 'エコシステム' } } }) })) storiesOf('Welcome', module).add('to Storybook', () => ({ components: { Welcome }, template: '', methods: { action: linkTo('Button') }, })) storiesOf('Button', module) .add('with text', () => ({ components: { MyButton }, template: 'Hello Button', methods: { action: action('clicked') }, })) .add('with some emoji', () => ({ components: { MyButton }, template: '😀 😎 👍 💯', methods: { action: action('clicked') }, })) ================================================ FILE: examples/storybook/webpack.config.js ================================================ var path = require('path') var webpack = require('webpack') module.exports = { entry: './src/main.js', output: { path: path.resolve(__dirname, './dist'), publicPath: '/dist/', filename: 'build.js' }, module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { } // other vue-loader options go here } }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }, { test: /\.(png|jpg|gif|svg)$/, loader: 'file-loader', options: { name: '[name].[ext]?[hash]' } } ] }, resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js' } }, devServer: { historyApiFallback: true, noInfo: true }, performance: { hints: false }, devtool: '#eval-source-map' } if (process.env.NODE_ENV === 'production') { module.exports.devtool = '#source-map' // http://vue-loader.vuejs.org/en/workflow/production.html module.exports.plugins = (module.exports.plugins || []).concat([ new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }), new webpack.optimize.UglifyJsPlugin({ sourceMap: true, compress: { warnings: false } }), new webpack.LoaderOptionsPlugin({ minimize: true }) ]) } ================================================ FILE: gitbook/LANGS.md ================================================ * [6.0+ Docs](en/) * [5.x Docs](old/) ================================================ FILE: gitbook/book.json ================================================ { "gitbook": "3.x.x", "title": "vue-i18n", "root": "./", "plugins": [ "edit-link", "theme-vuejs@git+https://github.com/pearofducks/gitbook-plugin-theme-vuejs.git", "-fontsettings", "github", "advanced-emoji@0.2.2" ], "pluginsConfig": { "edit-link": { "base": "https://github.com/kazupon/vue-i18n/tree/dev/gitbook", "label": "Edit This Page" }, "github": { "url": "https://github.com/kazupon/vue-i18n/" } }, "links": { "sharing": { "facebook": false, "twitter": false } } } ================================================ FILE: gitbook/en/SUMMARY.md ================================================ # vue-i18n documentation Internationalization plugin of Vue.js > :warning: NOTE: This documentation is for vue-i18n v6.0 or later. * [Looking for 5.x Docs?](https://kazupon.github.io/vue-i18n/old/) * [Release Notes](https://github.com/kazupon/vue-i18n/releases) * [Installation](installation.md) * [Getting started](started.md) * [Formatting](formatting.md) * [HTML formatting](formatting.md#html-formatting) * [Named formatting](formatting.md#named-formatting) * [List formatting](formatting.md#list-formatting) * [Support ruby on rails i18n format](formatting.md#support-ruby-on-rails-i18n-format) * [Custom formatting](formatting.md#custom-formatting) * [Pluralization](pluralization.md) * [DateTime localization](datetime.md) * [Number localization](number.md) * [Locale messages syntax](messages.md) * [Structure](messages.md#structure) * [Linked locale messages](messages.md#linked-locale-messages) * [Fallback localization](fallback.md) * [Component based localization](component.md) * [Custom directive localization](directive.md) * [String Syntax](directive.md#string-syntax) * [Object Syntax](directive.md#object-syntax) * [$t vs v-t](directive.md#t-vs-v-t) * [Component interpolation](interpolation.md) * [Basic Usage](interpolation.md#basic-usage) * [Advanced Usage](interpolation.md#advanced-usage) * [Single file components](sfc.md) * [Basic Usage](sfc.md#basic-usage) * [YAML loading](sfc.md#yaml-loading) * [Multiple custom blocks](sfc.md#multiple-custom-blocks) * [Hot reloading](hot-reload.md) * [Lazy loading](lazy-loading.md) * [Common gotchas](gotchas.md) * [Migrations](migrations.md) * [Global config](migrations.md#global-config) * [Global methods](migrations.md#global-methods) * [Constructor options](migrations.md#constructor-options) * [Instance properties](migrations.md#instance-properties) * [Features](migrations.md#features) * [API references](api.md) * [Extension of Vue](api.md#extension-of-vue) * [Vue constructor options](api.md#vue-constructor-options) * [Vue injected methods](api.md#vue-injected-methods) * [Injected properties](api.md#injected-properties) * [VueI18n class](api.md#vuei18n-class) * [Static properties](api.md#static-properties) * [Constructor options](api.md#constructor-options) * [Properties](api.md#properties) * [Methods](api.md#methods) * [Directives](api.md#directives) * [v-t](api.md#v-t) * [Components](api.md#components) * [i18n](api.md#i18n-functional-component) * [Special Attributes](api.md#special-attributes) * [place](api.md#place) ================================================ FILE: gitbook/en/api.md ================================================ # API references ## Extension of Vue ### Vue constructor options #### i18n - **Type:** `I18nOptions` Component based localization option. - **See also:** [`VueI18n` class constructor options](#constructor-options) ### Vue injected methods #### $t - **Arguments:** - `{Path} key`: required - `{Locale} locale`: optional - `{Array | Object} values`: optional - **Return:** `TranslateResult` Localize the locale message of `key`. Localize in preferentially component locale messages than global locale messages. If not specified component locale messages, localize with global locale messages. If you specified `locale`, localize the locale messages of `locale`. If you specified `key` of list / named formatting local messages, you must specify `values` too. For `values` more details see [Formatting](formatting.md). #### $tc - **Arguments:** - `{Path} key`: required - `{number} choice`: optional, default 1 - `{Locale} locale`: optional - `{string | Array | Object} values`: optional - **Return:** `string` Localize the locale message of `key` with pluralization. Localize in preferentially component locale messages than global locale messages. If not specified component locale messages, localize with global locale messages. If you specified `locale`, localize the locale messages of `locale`. If you will specify string value to `values`, localize the locale messages of value. If you will specify Array or Object value to `values`, you must specify with `values` of [$t](#t). If default pluralization does not suit your needs, see [pluralization rules in constructor options](#pluralizationrules) and [custom pluralization](pluralization.md). #### getChoiceIndex - **Arguments:** - `{number} choice` - `{number} choicesLength` - **Return:** `finalChoice {number}` Get pluralization index for current pluralizing number and a given amount of choices. Can be overriden through prototype mutation: ```js VueI18n.prototype.getChoiceIndex = /* custom implementation */ ``` #### $te - **Arguments:** - `{Path} key`: required - `{Locale} locale`: optional - **Return:** `boolean` Check whether key exists. In Vue instance, If not specified component locale messages, check with global locale messages. If you specified `locale`, check the locale messages of `locale`. #### $d > :new: 7.0+ - **Arguments:** - `{number | Date} value`: required - `{Path | Object} key`: optional - `{Locale | Object} locale`: optional - **Return:** `DateTimeFormatResult` Localize the datetime of `value` with datetime format of `key`. The datetime format of `key` need to register to `dateTimeFormats` option of `VueI18n` class, and depend on `locale` option of `VueI18n` constructor. If you will specify `locale` argument, it will have priority over `locale` option of `VueI18n` constructor. If the datetime format of `key` not exist in `dateTimeFormats` option, fallback to depend on `fallbackLocale` option of `VueI18n` constructor. #### $n > :new: 7.0+ - **Arguments:** - `{number} value`: required - `{Path | Object} key`: optional - `{Locale} locale`: optional - **Return:** `NumberFormatResult` Localize the number of `value` with number format of `key`. The number format of `key` need to register to `numberFormats` option of `VueI18n` class, and depend on `locale` option of `VueI18n` constructor. If you will specify `locale` argument, it will have priority over `locale` option of `VueI18n` constructor. If the number format of `key` not exist in `numberFormats` option, fallback to depend on `fallbackLocale` option of `VueI18n` constructor. If the second `key` argument specified as an object, it should have the following properties: - `key {Path}`: optional, number format - `locale {Locale}`: optional, locale - `compactDisplay {string}`: optional, number format option - `currency {string}`: optional, number format option - `currencyDisplay {string}`: optional, number format option - `currencySign {string}`: optional, number format option - `localeMatcher {string}`: optional, number format option - `notation {string}`: optional, number format option - `numberingSystem {string}`: optional, number format option - `signDisplay {string}`: optional, number format option - `style {string}`: optional, number format option - `unit {string}`: optional, number format option - `unitDisplay {string}`: optional, number format option - `useGrouping {boolean}`: optional, number format option - `minimumIntegerDigits {string}`: optional, number format option - `minimumFractionDigits {string}`: optional, number format option - `maximumFractionDigits {string}`: optional, number format option - `minimumSignificantDigits {string}`: optional, number format option - `maximumSignificantDigits {string}`: optional, number format option Any specified number format options will have priority over `numberFormats` of `VueI18n` constructor. ### Injected properties #### $i18n - **Type:** `I18n` - **Read only** Get a `VueI18n` instance. If you are specify. If you have specified an `i18n` option at component options, you will be able to get a `VueI18n` instance at the component, Otherwise, you will be able get root `VueI18n` instance. ## `VueI18n` class `Vuei18n` class implement `I18n` interface of [flowtype definitions](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js) ### Static properties #### version - **Type:** `string` vue-i18n version. #### availabilities > :new: 7.0+ - **Type:** `IntlAvailability` Whether the following internationalization features are available: - `{boolean} dateTimeFormat`: locale sensitive datetime formatting - `{boolean} numberFormat`: locale sensitive number formatting The above internationalization features are depends on [the browser environments](http://kangax.github.io/compat-table/esintl/), due to implement with ECMAScript Internationalization API (ECMA-402). ### Constructor options You can specify the below some options of `I18nOptions` constructor options of [flowtype definitions](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js) #### locale - **Type:** `Locale` - **Default:** `'en-US'` The locale of localization. If the locale contains a territory and a dialect, this locale contains an implicit fallback. #### fallbackLocale - **Type:** `Locale` - **Default:** `'en-US'` The locale of fallback localization. For more complex fallback definitions see fallback. #### messages - **Type:** `LocaleMessages` - **Default:** `{}` The locale messages of localization. #### dateTimeFormats > :new: 7.0+ - **Type:** `DateTimeFormats` - **Default:** `{}` The datetime formats of localization. - **See also:** `DateTimeFormats` type of [flowtype definitions](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js) #### numberFormats > :new: 7.0+ - **Type:** `NumberFormats` - **Default:** `{}` The number formats of localization. - **See also:** `NumberFormats` type of [flowtype definitions](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js) #### formatter - **Type:** `Formatter` - **Default:** Built in formatter The formatter that implemented with `Formatter` interface. #### missing - **Type:** `MissingHandler` - **Default:** `null` A handler for localization missing. The handler gets called with the localization target locale, localization path key and the Vue instance. If missing handler is assigned, and occurred localization missing, it's not warned. #### fallbackRoot - **Type:** `Boolean` - **Default:** `true` In the component localization, whether to fall back to root level (global) localization when localization fails. If `false`, it's warned, and is returned the key. #### fallbackRootWithEmptyString > :new: 8.26+ - **Type:** `Boolean` - **Default:** `true` In the component localization, whether to fall back to root level (global) localization when local message is an empty string. Please note the default behavior in vue-i18n 9.x is to not falling back to root for local message that is empty string. If `false`, the empty local message will not fall back to root and will be kept as empty string. #### sync - **Type:** `Boolean` - **Default:** `true` Whether synchronize the root level locale to the component localization locale. If `false`, regardless of the root level locale, localize for each component locale. #### silentTranslationWarn > 6.1+ - **Type:** `Boolean` - **Default:** `false` Whether suppress warnings outputted when localization fails. If `true`, supress localization fail warnings. #### pluralizationRules > 8.5+ - **Type:** `Object` - **Default:** `{}` A set of rules for word pluralization in a following format: ```js { // Key - locale for the rule to be applied to. // Value - mapping function that maps a choice index from `$tc` to the actual choice of the plural word. 'ru': function (choice, choiceIndex) => Number/* index of the plural word */; 'en': function (choice, choiceIndex) => Number/* index of the plural word */; 'jp': function (choice, choiceIndex) => Number/* index of the plural word */; } ``` ### Properties #### locale - **Type:** `Locale` - **Read/Write** The locale of localization. #### fallbackLocale - **Type:** `Locale` - **Read/Write** The locale of fallback localization. #### messages - **Type:** `LocaleMessages` - **Read only** The locale messages of localization. #### dateTimeFormats > :new: 7.0+ - **Type:** `DateTimeFormats` - **Read only** The datetime formats of localization. #### numberFormats > :new: 7.0+ - **Type:** `NumberFormats` - **Read only** The number formats of localization. #### missing - **Type:** `MissingHandler` - **Read/Write** A handler for localization missing. #### formatter - **Type:** `Formatter` - **Read/Write** The formatter that implemented with `Formatter` interface. #### silentTranslationWarn > 6.1+ - **Type:** `boolean` - **Read/Write** Whether suppress warnings outputted when localization fails. #### pluralizationRules > 8.5+ - **Type:** `Object` - **Default:** `{}` A set of rules for word pluralization. Key is a locale, value is the rule function for that locale. ### Methods #### getLocaleMessage( locale ) - **Arguments:** - `{Locale} locale` - **Return:** `LocaleMessageObject` Get the locale message of locale. #### setLocaleMessage( locale, message ) - **Arguments:** - `{Locale} locale` - `{LocaleMessageObject} message` Set the locale message of locale. #### mergeLocaleMessage( locale, message ) > 6.1+ - **Arguments:** - `{Locale} locale` - `{LocaleMessageObject} message` Merge the registered locale messages with the locale message of locale. #### t( key, [locale], [values] ) - **Arguments:** - `{Path} key`: required - `{Locale} locale`: optional - `{Array | Object} values`: optional - **Return:**: `TranslateResult` This is the same as the `Function` returned with `$t` method. More detail see [$t](#$t). #### i( key, [locale], [values] ) > :new: 7.0+ - **Arguments:** - `{Path} key`: required - `{Locale} locale`: optional - `{Array} values`: optional - **Return:**: `TranslateResult` #### tc( key, [choice], [values] ) - **Arguments:** - `{Path} key`: required - `{number} choice`: optional, default `1` - `{string | Array | Object} values`: optional - **Return:** `TranslateResult` This is the same as the `Function` returned `$tc` method. More detail see [$tc](#$tc). #### te( key, [locale] ) - **Arguments:** - `{string} key`: required - `{Locale} locale`: optional - **Return:** `boolean` Check whether key path exists in global locale message. If you specified `locale`, check the locale message of `locale`. #### getDateTimeFormat ( locale ) > :new: 7.0+ - **Arguments:** - `{Locale} locale` - **Return:** `DateTimeFormat` Get the datetime format of locale. #### setDateTimeFormat ( locale, format ) > :new: 7.0+ - **Arguments:** - `{Locale} locale` - `{DateTimeFormat} format` Set the datetime format of locale. #### mergeDateTimeFormat ( locale, format ) > :new: 7.0+ - **Arguments:** - `{Locale} locale` - `{DateTimeFormat} format` Merge the registered datetime formats with the datetime format of locale. #### d( value, [key], [locale] ) > :new: 7.0+ - **Arguments:** - `{number | Date} value`: required - `{Path | Object} key`: optional - `{Locale | Object} locale`: optional - **Return:** `DateTimeFormatResult` This is the same as `$d` method of Vue instance method. More detail see [$d](#$d). #### getNumberFormat ( locale ) > :new: 7.0+ - **Arguments:** - `{Locale} locale` - **Return:** `NumberFormat` Get the number format of locale. #### setNumberFormat ( locale, format ) > :new: 7.0+ - **Arguments:** - `{Locale} locale` - `{NumberFormat} format` Set the number format of locale. #### mergeNumberFormat ( locale, format ) > :new: 7.0+ - **Arguments:** - `{Locale} locale` - `{NumberFormat} format` Merge the registered number formats with the number format of locale. #### n( value, [key], [locale] ) > :new: 7.0+ - **Arguments:** - `{number} value`: required - `{Path | Object} key`: optional - `{Locale} locale`: optional - **Return:** `NumberFormatResult` This is the same as `$n` method of Vue instance method. More detail see [$n](#$n). ## Directives > :new: 7.3+ ### v-t - **Expects:** `string | Object` - **Details:** Update the element `textContent` that localized with locale messages. You can use string syntax or object syntax. string syntax can be specified as a keypath of locale messages. If you can be used object syntax, you need to specify as the object key the following params: - path: required, key of locale messages - locale: optional, locale - args: optional, for list or named formatting - choice: optional, for pluralization - **Examples:** ```html

``` - **See also:** [Custom directive localization](./directive.md) ## Components ### i18n functional component > :new: 7.0+ #### Props: - `path {Path}`: required, keypath of locale messages - `locale {Locale}`: optional, locale - `tag {string}`: optional, default `span` - `places {Array | Object}`: optional (7.2+) #### Usage: ```html ``` ```javascript const messages = { en: { tos: 'Term of Service', term: 'I accept xxx {0}.' }, ja: { tos: '利用規約', term: '私は xxx の{0}に同意します。' } } const i18n = new VueI18n({ locale: 'en', messages }) new Vue({ i18n, data: { url: '/term' } }).$mount('#app') ``` #### See also: [Component interpolation](./interpolation.md) ## Special Attributes ### place > :new: 7.2+ #### Expects: `{number | string}` Used on component interpolation to indicate which index of list formatting or key of named formatting. For detailed usage, see the guide section linked below. #### See also: [Component interpolation](./interpolation.md) ================================================ FILE: gitbook/en/component.md ================================================ # Component based localization In general, locale info (e.g. `locale`,`messages`, etc) is set as a constructor option of the `VueI18n` instance and it sets the `i18n` option as a root Vue instance. This means you can use translations globally by using `$t` or `$tc` in the root Vue instance and any composed component. You can also manage locale info for each component separately, which might be more convenient due to Vues component oriented design. Component based localization example: ```javascript // setup locale info for root Vue instance const i18n = new VueI18n({ locale: 'ja', messages: { en: { message: { hello: 'hello world', greeting: 'good morning' } }, ja: { message: { hello: 'こんにちは、世界', greeting: 'おはようございます' } } } }) // Define component const Component1 = { template: `

Component1 locale messages: {{ $t("message.hello") }}

Fallback global locale messages: {{ $t("message.greeting") }}

`, i18n: { // `i18n` option messages: { en: { message: { hello: 'hello component1' } }, ja: { message: { hello: 'こんにちは、component1' } } } } } new Vue({ i18n, components: { Component1 } }).$mount('#app') ``` Template: ```html

{{ $t("message.hello") }}

``` Outputs the following: ```html

こんにちは、世界

Component1 locale messages: こんにちは、component1

Fallback global locale messages: おはよう、世界!

``` As in the example above, if the component doesn't have the locale message, it falls back to globally defined localization info. The component uses the language set in the root instance (in the above example: `locale: 'ja'`). If you hope localize in the component locale, you can realize with `sync: false` and `locale` in `i18n` option. ================================================ FILE: gitbook/en/datetime.md ================================================ # DateTime localization > :new: 7.0+ You can localize the datetime with your definition formats. DateTime formats the below: ```javascript const dateTimeFormats = { 'en-US': { short: { year: 'numeric', month: 'short', day: 'numeric' }, long: { year: 'numeric', month: 'short', day: 'numeric', weekday: 'short', hour: 'numeric', minute: 'numeric' } }, 'ja-JP': { short: { year: 'numeric', month: 'short', day: 'numeric' }, long: { year: 'numeric', month: 'short', day: 'numeric', weekday: 'short', hour: 'numeric', minute: 'numeric', hour12: true } } } ``` As the Above, You can define the datetime format with named (e.g. `short`, `long`, etc), and you need to use [the options with ECMA-402 Intl.DateTimeFormat](http://www.ecma-international.org/ecma-402/2.0/#sec-intl-datetimeformat-constructor) After that like the locale messages, You need to specify the `dateTimeFormats` option of `VueI18n` constructor: ```javascript const i18n = new VueI18n({ dateTimeFormats }) new Vue({ i18n }).$mount('#app') ``` Template the below: ```html

{{ $d(new Date(), 'short') }}

{{ $d(new Date(), 'long', 'ja-JP') }}

``` Output the below: ```html

Apr 19, 2017

2017年4月19日(水) 午前2:19

``` ================================================ FILE: gitbook/en/directive.md ================================================ # Custom directive localization > :new: 7.3+ You can translate not only with `v-t` custom directive, but also with `$t` method. ## String syntax You can pass the keypath of locale messages with string syntax. Javascript: ```javascript new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { hello: 'hi there!' }, ja: { hello: 'こんにちは!' } } }), data: { path: 'hello' } }).$mount('#string-syntax') ``` Templates: ```html

``` Outputs: ```html

hi there!

hi there!

``` ## Object syntax You can use object syntax. Javascript: ```javascript new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { hello: 'hi {name}!' }, ja: { hello: 'こんにちは、{name}!' } } }), computed: { nickName () { return 'kazupon' } }, data: { path: 'hello' } }).$mount('#object-syntax') ``` Templates: ```html

``` Outputs: ```html

こんにちは、kazupon!

hi kazupon!

``` ## `$t` vs `v-t` ### `$t` `$t` is extended Vue instance method. It has the following pros and cons: #### Pros You can **flexibly** use mustash syntax `{{}}` in templates and also computed props and methods in Vue instance. #### Cons `$t` is executed **every time** when re-render occurs, so it does have a translation costs. ### `v-t` `v-t` is a custom directive. It has the following pros and cons: #### Pros `v-t` has **better performance** than the `$t` method due to its cache with the custom directive, when translated once. Also, pre-translation is possible with the Vue compiler module which was provided by [`vue-i18n-extensions`](https://github.com/kazupon/vue-i18n-extensions). Therefore it's possible to make **more performance optimizations**. #### Cons `v-t` can not be flexibly used like `$t`, it's rather **complex**. The translated content with `v-t` is inserted into the `textContent` of the element. Also, when you use server-side rendering, you need to set the [custom directive](https://github.com/kazupon/vue-i18n-extensions#directive-v-t-custom-directive-for-server-side) to `directives` option of the `createRenderer` function. ================================================ FILE: gitbook/en/fallback.md ================================================ # Fallback localization ## Implicit fallback using locales If a `locale` is given containing a territory and an optional dialect, the implicit fallback is activated automatically. For example `de-DE-bavarian` would fallback 1. `de-DE-bavarian` 1. `de-DE` 1. `de` To supress the automatic fallback, add the postfix exclamation mark `!`, for example `de-DE!` ## Explicit fallback with one locale When the `message` key does not exist in the `ja` locale: ```javascript const messages = { en: { message: 'hello world' }, ja: { } } ``` And you have specified a `fallbackLocale` in the VueI18n constructor option, the `message` key uses the corresponding `en` locale key as a fallback: ```javascript const i18n = new VueI18n({ locale: 'ja', fallbackLocale: 'en', messages }) ``` When you template the below: ```html

{{ $t('message') }}

``` The following will be the output: ```html

hello world

``` ## Explicit fallback with an array of locales It is possible to set more than one fallback locale by using an array of locales. For example ```javascript fallbackLocale: [ 'fr', 'en' ], ``` ## Explicit fallback with decision maps If more complex decision maps for fallback locales are required, it is possible to define decision maps with according fallback locales. Using the following decision map ```javascript fallbackLocale: { /* 1 */ 'de-CH': ['fr', 'it'], /* 2 */ 'zh-Hant': ['zh-Hans'], /* 3 */ 'es-CL': ['es-AR'], /* 4 */ 'es': ['en-GB'], /* 5 */ 'pt': ['es-AR'], /* 6 */ 'default': ['en', 'da'] }, ``` will result in the following fallback chains | locale | fallback chains | |--------|-----------------| | `'de-CH'` | de-CH > fr > it > en > da | | `'de'` | de > en > da | | `'zh-Hant'` | zh-Hant > zh-Hans > zh > en > da | | `'es-SP'` | es-SP > es > en-GB > en > da | | `'es-SP!'` | es-SP > en > da | | `'fr'` | fr > en > da | | `'pt-BR'` | pt-BR > pt > es-AR > es > en-GB > en > da | | `'es-CL'` | es-CL > es-AR > es > en-GB > en > da | ================================================ FILE: gitbook/en/formatting.md ================================================ # Formatting ## HTML formatting > :warning: NOTE: Dynamically localization arbitrary HTML on your website can be very dangerous because it can easily lead to XSS vulnerabilities. Only use HTML interpolation on trusted content and never on user-provided content. We recommended that use [component interpolation](./interpolation.md) feature. In some cases you might want to render your translation as an HTML message and not a static string. ```javascript const messages = { en: { message: { hello: 'hello
world' } } } ``` Template the below: ```html

``` Output the below (instead of the message pre formatted) ```html

hello world

``` ## Named formatting Locale messages the below: ```javascript const messages = { en: { message: { hello: '{msg} world' } } } ``` Template the below: ```html

{{ $t('message.hello', { msg: 'hello' }) }}

``` Output the below: ```html

hello world

``` ## List formatting Locale messages the below: ```javascript const messages = { en: { message: { hello: '{0} world' } } } ``` Template the below: ```html

{{ $t('message.hello', ['hello']) }}

``` Output the below: ```html

hello world

``` List formatting also accepts array-like object: ```html

{{ $t('message.hello', {'0': 'hello'}) }}

``` Output the below: ```html

hello world

``` ## Support ruby on rails i18n format Locale messages the below: ```javascript const messages = { en: { message: { hello: '%{msg} world' } } } ``` Template the below: ```html

{{ $t('message.hello', { msg: 'hello' }) }}

``` Output the below: ```html

hello world

``` ## Custom formatting Sometimes, you maybe need to translate with custom formatting (e.g. [ICU message syntax](http://userguide.icu-project.org/formatparse/messages)). You can realize with custom formatter that implement [Formatter Interface](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js#L41-L43). The following custom formatter with ES2015 class syntax: ```javascript // Custom Fromatter implementation class CustomFormatter { constructor (options) { // ... } // // interpolate // // @param {string} message // string of list or named format. // e.g. // - named formatting: 'Hi {name}' // - list formatting: 'Hi {0}' // // @param {Object | Array} values // values of `message` interpolation. // passed values with `$t`, `$tc` and `i18n` functional component. // e.g. // - $t('hello', { name: 'kazupon' }) -> passed values: Object `{ name: 'kazupon' }` // - $t('hello', ['kazupon']) -> passed values: Array `['kazupon']` // - `i18n` functional component (component interpolation) // //

kazupon

//

how are you?

//
// -> passed values: Array (included VNode): // `[VNode{ tag: 'p', text: 'kazupon', ...}, VNode{ tag: 'p', text: 'how are you?', ...}]` // // @param {string} path // a path to the message, as passed into the $t/t() functions. // - $t('hello.louis') -> path === 'hello.louis' // // @return {Array} // interpolated values. you need to return the following: // - array of string, when is using `$t` or `$tc`. // - array included VNode object, when is using `i18n` functional component. // - null - if you want the default vue-i18n formatter to handle the case // interpolate (message, values, path) { // implement interpolation logic here // ... // return the interpolated array return ['resolved message string'] } } // register with `formatter` option const i18n = new VueI18n({ locale: 'en-US', formatter: new CustomFormatter(/* here the constructor options */), messages: { 'en-US': { // ... }, // ... } }) // Run! new Vue({ i18n }).$mount('#app') ``` You can check [the custom formatter official example](https://github.com/kazupon/vue-i18n/tree/dev/examples/formatting/custom). ================================================ FILE: gitbook/en/gotchas.md ================================================ # Common gotchas ## About localize with `data` option The `data` option of Vue Component evaluate once only when Vue Component is instantiated. For this reason, if you localize with `$t`, `$n` and `$d` inside this option, cannot change it with `locale` of VueI18n instance (e.g. vm.$i18n.locale). As a workaround, you can use the computed props. ================================================ FILE: gitbook/en/hot-reload.md ================================================ # Hot reloading You can watch for changes in localization files and hot reload changes into your application. ```javascript // Something locale messages const messages = { // ... } // VueI18n instance const i18n = new Vuei18n({ locale: 'en', messages }) // Run app const app = new Vue({ i18n, // ... }).$mount('#app') // Hot updates if (module.hot) { module.hot.accept(['./en', './ja'], function () { i18n.setLocaleMessage('en', require('./en').default) i18n.setLocaleMessage('ja', require('./ja').default) // Or the following hot updates via $i18n property // app.$i18n.setLocaleMessage('en', require('./en').default) // app.$i18n.setLocaleMessage('ja', require('./ja').default) }) } ``` ================================================ FILE: gitbook/en/installation.md ================================================ # Installation ### Compatibility Note - Vue.js `2.0.0`+ ### Direct Download / CDN https://unpkg.com/vue-i18n/dist/vue-i18n [unpkg.com](https://unpkg.com) provides NPM-based CDN links. The above link will always point to the latest release on NPM. You can also use a specific version/tag via URLs like https://unpkg.com/vue-i18n@7.7.0/dist/vue-i18n.js Include vue-i18n after Vue and it will install itself automatically: ```html ``` ### NPM $ npm install vue-i18n ### Yarn $ yarn add vue-i18n When using with a module system, you must explicitly install the `vue-i18n` via `Vue.use()`: ```javascript import Vue from 'vue' import VueI18n from 'vue-i18n' Vue.use(VueI18n) ``` You don't need to do this when using global script tags. ### Dev Build You will have to clone directly from GitHub and build `vue-i18n` yourself if you want to use the latest dev build. $ git clone https://github.com/kazupon/vue-i18n.git node_modules/vue-i18n $ cd node_modules/vue-i18n $ npm install $ npm run build ================================================ FILE: gitbook/en/interpolation.md ================================================ # Component interpolation ## Basic Usage > :new: 7.0+ Sometimes, we need to localize with locale message that was included HTML tag or component. For example: ```html

I accept xxx Terms of Service Agreement

``` In the above message, if you use with `$t`, probably you may try to compose the following locale messages: ```javascript const messages = { en: { term1: 'I Accept xxx\'s', term2: 'Terms of Service Agreement' } } ``` And in the following, you may try to implement in template: ```html

{{ $t('term1') }}{{ $t('term2') }}

``` output: ```html

I accept xxx Terms of Service Agreement

``` This is very cumbersome, and if you configure the `` tag in a locale message, there is a possibility XSS vulnerabilities due to localize with `v-html="$t('term')"`. You can avoid it with using `i18n` functional component. For example: ```html ``` ```javascript const messages = { en: { tos: 'Term of Service', term: 'I accept xxx {0}.' }, ja: { tos: '利用規約', term: '私は xxx の{0}に同意します。' } } const i18n = new VueI18n({ locale: 'en', messages }) new Vue({ i18n, data: { url: '/term' } }).$mount('#app') ``` the following output: ```html
``` About the above example, see the [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/interpolation) The children of `i18n` functional component is interpolated with locale message of `path` prop. In the above example, `{{ $t('tos') }}` is interpolated with `term` locale message. In above example, the component interpolation follows the **list formatting**. The children of `i18n` functional component are interpolated by their orders of appearance. ## Advanced Usage > :new: 7.2+ > :warning: NOTE: In `i18n` component, text content consists of only white spaces will be omitted. Named formatting is supported with the help of `place` attribute. For example: ```html
{{ changeLimit }} {{ $t('change') }}
``` ```javascript const messages = { en: { info: 'You can {action} until {limit} minutes from departure.', change: 'change your flight', refund: 'refund the ticket' } } const i18n = new VueI18n({ locale: 'en', messages }) new Vue({ i18n, data: { changeUrl: '/change', refundUrl: '/refund', changeLimit: 15, refundLimit: 30 } }).$mount('#app') ``` Outputs: ```html

You can change your flight until 15 minutes from departure.

``` > :warning: NOTE: To use named formatting, all children of `i18n` component must have `place` attribute set. Otherwise it will fallback to list formatting. If you still want to interpolate text content in named formatting, you could define `places` property on `i18n` component. For example: ```html ``` Outputs: ```html

You can refund your ticket until 30 minutes from departure.

``` ================================================ FILE: gitbook/en/lazy-loading.md ================================================ # Lazy loading translations Loading all of your translation files at once is overkill and unnecessary. Lazy loading or asynchronously loading the translation files is really easy when using Webpack. Lets assume we have a project directory similar to the one bellow ``` our-cool-project -dist -src --routes --store --setup ---i18n-setup.js --lang ---en.js ---it.js ``` The `lang` folder is where all of our translation files will reside. The `setup` folder is where our arbitrary setup files like the i18n-setup, global component inits, plugin inits and other reside. ```js //i18n-setup.js import Vue from 'vue' import VueI18n from 'vue-i18n' import messages from '@/lang' import axios from 'axios' Vue.use(VueI18n) export const i18n = new VueI18n({ locale: 'en', // set locale fallbackLocale: 'en', messages // set locale messages }) const loadedLanguages = ['en'] // our default language that is preloaded function setI18nLanguage (lang) { i18n.locale = lang axios.defaults.headers.common['Accept-Language'] = lang document.documentElement.lang = lang return lang } export function loadLanguageAsync (lang) { if (i18n.locale !== lang) { if (!loadedLanguages.includes(lang)) { return import(/* webpackChunkName: "lang-[request]" */ `@/lang/${lang}`).then(msgs => { i18n.setLocaleMessage(lang, msgs.default) loadedLanguages.push(lang) return setI18nLanguage(lang) }) } return Promise.resolve(setI18nLanguage(lang)) } return Promise.resolve(lang) } ``` In short we are creating a new VueI18n instance as we normally would. Then we are creating a `loadedLanguages` array that will keep track of our loaded languages. Next is the `setI18nLanguage` function that will actually change the language in our vueI18n instance, axios and where ever else is needed. The `loadLanguageAsync` function is what we will actually use to change the languages. Loading the new files is done via the `import` function, which is generously provided by Webpack and it allows us to load files dynamically, and because it uses promises we can easily wait for the loading to finish. You can learn more about the import function in the [Webpack documentation](https://webpack.js.org/guides/code-splitting/#dynamic-imports). Using the `loadLanguageAsync` function is straight forward. A common use case is inside a vue-router beforeEach hook. ```js router.beforeEach((to, from, next) => { const lang = to.params.lang loadLanguageAsync(lang).then(() => next()) }) ``` We could improve this by checking if the `lang` is actually supported by us or not, call `reject` so we can catch that in the `beforeEach` stopping the route transition. ================================================ FILE: gitbook/en/messages.md ================================================ # Locale messages syntax ## Structure Locale Messages syntax the below: ```javascript // As Flowtype definition, Locale Messages syntax like BNF annotation type LocaleMessages = { [key: Locale]: LocaleMessageObject }; type LocaleMessageObject = { [key: Path]: LocaleMessage }; type LocaleMessageArray = LocaleMessage[]; type LocaleMessage = string | LocaleMessageObject | LocaleMessageArray; type Locale = string; type Path = string; ``` Based on the above syntax, You can configure the following structure Locale Messages: ```json { "en": { // 'en' Locale "key1": "this is message1", // basic "nested": { // nested "message1": "this is nested message1" }, errors: [ // array "this is 0 error code message", { // object in array "internal1": "this is internal 1 error message" }, [ // array in array "this is nested array error 1" ] ] }, "ja": { // 'ja' Locale // ... } } ``` In the above Locale Messages structure, You can translate with using below key paths. ```html

{{ $t('key1') }}

{{ $t('nested.message1') }}

{{ $t('errors[0]') }}

{{ $t('errors[1].internal1') }}

{{ $t('errors[2][0]') }}

``` Output the following: ```html

this is message1

this is nested message1

this is 0 error code message

this is internal 1 error message

this is nested array error 1

``` ## Linked locale messages If there's a translation key that will always have the same concrete text as another one you can just link to it. To link to another translation key, all you have to do is to prefix its contents with an `@:` sign followed by the full name of the translation key including the namespace you want to link to. Locale messages the below: ```javascript const messages = { en: { message: { the_world: 'the world', dio: 'DIO:', linked: '@:message.dio @:message.the_world !!!!' } } } ``` Template the below: ```html

{{ $t('message.linked') }}

``` Output the below: ```html

DIO: the world !!!!

``` ### Formatting linked locale messages If the language distinguish cases of character, you may need control the case of the linked locale messages. Linked messages can be formatted with modifier `@.MODIFIER:key` The below modifiers are available currently. * `upper`: Uppercase all characters in the linked message. * `lower`: Lowercase all characters in the linked message. * `capitalize`: Uppercase the first char in the linked message. Locale messages the below: ```javascript const messages = { en: { message: { homeAddress: 'Home address', phoneNumber: 'phone number', missingHomeAddress: 'Please provide @.lower:message.homeAddress and @:message.phoneNumber', phoneNbField: '@.capitalize:message.phoneNumber :' phoneNbPlaceholder: 'Please enter your @:message.phoneNumber ...' } } } ``` ```html

{{ $t('message.missingHomeAddress') }}

``` Output the below: ```html

Please provide home address

``` ### Grouping by brackets A translation key of linked locale message can also have the form of `@:(message.foo.bar.baz)` in which the link to another translation key is within brackets `()`. This can be useful if the link `@:message.something` is following by period `.`, which can be a part of link but in case it should not be. Locale messages the below: ```js const messages = { en: { message: { dio: 'DIO', linked: 'There\'s a reason, you lost, @:(message.dio).' } } } ``` Template the below: ```html

{{ $t('message.linked') }}

``` Output the below: ```html

There's a reason, you lost, DIO.

``` ================================================ FILE: gitbook/en/migrations.md ================================================ # Migrations ## Global config ### lang replaced Use `VueI18n` class constructor `locale` option, or `VueI18n#locale` property: ```javascript const i18n = new VueI18n({ locale: 'en', // ... }) const app = new Vue({ i18n }).$mount('#app') // change locale i18n.locale = 'ja' // or app.$i18n.locale = 'ja' ``` ### fallbackLang replaced Use `VueI18n` class constructor `fallbackLocale` option, or `VueI18n#fallbackLocale` property: ```javascript const i18n = new VueI18n({ locale: 'ja', fallbackLocale: 'en', // ... }) const app = new Vue({ i18n }).$mount('#app') // change fallback locale i18n.fallbackLocale = 'zh' // or app.$i18n.fallbackLocale = 'zh' ``` ### missingHandler replaced Use `VueI18n` class constructor `missing` option, or `VueI18n#missing` property: ```javascript const i18n = new VueI18n({ // ... missing: (locale, key, vm) => { // handle translation missing }, // ... }) const app = new Vue({ i18n }).$mount('#app') // change missing handler i18n.missing = (locale, key, vm) => { // handle translation missing } // or app.$i18n.missing = (locale, key, vm) => { // handle translation missing } ``` ### i18nFormatter replaced Use `VueI18n` class constructor `formatter` option, or `VueI18n#formatter` property: ```javascript class CustomFormatter { format (message, ...values) { // something render logic return 'something string' } } const i18n = new VueI18n({ // ... formatter: new CustomFormatter(), // ... }) const app = new Vue({ i18n }).$mount('#app') // change custom formatter i18n.formatter = { format: (message, ...values) => { // something render logic return 'something string' } } // or app.$i18n.formatter = { format: (message, ...values) => { // something render logic return 'something string' } } ``` ## Global methods ### Vue.locale replaced Use `VueI18n` class constructor `messages` option, or `VueI18n#GetLocaleMessage` / `VueI18n#setLocaleMessage` methods: ```javascript const i18n = new VueI18n({ // ... messages: { en: { hello: 'hello world', // ... }, ja: { hello: 'こんにちは、世界', // ... } }, // ... }) const app = new Vue({ i18n }).$mount('#app') // get locale message const en = i18n.getLocaleMessage('en') en.greeting = 'hi!' // set locale message i18n.setLocaleMessage('en', en) // or const ja = app.$i18n.getLocaleMessage('ja') ja.greeting = 'やあ!' app.$i18n.setLocaleMessage('ja', ja) ``` ### Vue.t replaced Use `VueI18n#t` method: ```javascript const i18n = new VueI18n({ locale: 'en', messages: { en: { greeting: 'hi {name}' } }, // ... }) i18n.t('greeting', { name: 'kazupon' }) // -> hi kazupon ``` ### Vue.tc replaced Use `VueI18n#tc` method: ```javascript const i18n = new VueI18n({ locale: 'en', messages: { en: { apple: 'no apples | one apple | {count} apples' } }, // ... }) const count = 10 i18n.tc('apple', count, { count }) // -> 10 apples ``` ### Vue.te replaced Use `VueI18n#te` method: ```javascript const i18n = new VueI18n({ locale: 'en', messages: { en: { hello: 'hello world' } }, // ... }) i18n.te('hello') // -> true i18n.te('hallo', 'ja') // -> false i18n.te('hello') // -> true ``` ## Constructor options ### locales replaced Use `messages` of `VueI18n` class constructor option, or `messages` of `i18n` option (for Component option): ```javascript const i18n = new VueI18n({ locale: 'en', messages: { en: { greeting: 'hi {name}' } }, // ... }) // for Component const Component1 = { i18n: { messages: { en: { title: 'Title1' } } } } ``` ## Instance properties ### $lang replaced Use `VueI18n#locale` property: ```javascript const i18n = new VueI18n({ locale: 'en', // ... }) const app = new Vue({ i18n }).$mount('#app') // change locale i18n.locale = 'ja' // or app.$i18n.locale = 'ja' ``` ## Features ### Dynamic locale removed If you need to dynamic set locale messages, you should implement the below: ```javascript const i18n = new VueI18n({ locale: 'en' }) const app = new Vue({ i18n, data: { loading: '' } }).$mount('#app') function loadLocaleMessage (locale, cb) { return fetch('/locales/' + locale, { method: 'get', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' } }).then((res) => { return res.json() }).then((json) => { if (Object.keys(json).length === 0) { return Promise.reject(new Error('locale empty !!')) } else { return Promise.resolve(json) } }).then((message) => { cb(null, message) }).catch((error) => { cb(error) }) } app.loading = 'loading ...' loadLocaleMessage('en', (err, message) => { if (err) { app.loading = '' console.error(err) return } i18n.setLocaleMessage('en', message) app.loading = '' }) ``` ================================================ FILE: gitbook/en/number.md ================================================ # Number localization > :new: 7.0+ You can localize the number with your definition formats. Number formats the below: ```javascript const numberFormats = { 'en-US': { currency: { style: 'currency', currency: 'USD' } }, 'ja-JP': { currency: { style: 'currency', currency: 'JPY', currencyDisplay: 'symbol' } } } ``` As the Above, You can define the number format with named (e.g. `currency`, etc), and you need to use [the options with ECMA-402 Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat) After that like the locale messages, You need to specify the `numberFormats` option of `VueI18n` constructor: ```javascript const i18n = new VueI18n({ numberFormats }) new Vue({ i18n }).$mount('#app') ``` Template the below: ```html

{{ $n(100, 'currency') }}

{{ $n(100, 'currency', 'ja-JP') }}

``` Output the below: ```html

$100.00

¥100

``` ================================================ FILE: gitbook/en/pluralization.md ================================================ # Pluralization You can translate with pluralization. You must define the locale that have a pipe `|` separator, and define plurals in pipe separator. Make your your local strings like this: ```javascript const messages = { en: { car: 'car | cars', apple: 'no apples | one apple | {count} apples' } } ``` Where the format is `0 things | 1 thing | more than 1 thing`. Your template needs to use `$tc()`, not `$t()`: ```html

{{ $tc('car', 1) }}

{{ $tc('car', 2) }}

{{ $tc('apple', 0) }}

{{ $tc('apple', 1) }}

{{ $tc('apple', 10, { count: 10 }) }}

``` Note that if you need to pass in a variable, you might have to pass it in twice like in the last example above. This will output the following HTML: ```html

car

cars

no apples

one apple

10 apples

``` --- ## Custom pluralization Such pluralization, however, does not apply to all languages (Slavic languages, for example, have different pluralization rules). In order to implement these rules you can pass an optional `pluralizationRules` object into `VueI18n` constructor options. Very simplified example using rules for Slavic languages (Russian, Ukrainian, etc.): ```js new VueI18n({ pluralizationRules: { /** Key - language to use the rule for, 'ru', in this case */ /** Value - function * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)` * @param choicesLength {number} an overall amount of available choices * @returns a final choice index to select plural word by **/ 'ru': function (choice, choicesLength) { // this === VueI18n instance, so the locale property also exists here if (choice === 0) { return 0; } const teen = choice > 10 && choice < 20; const endsWithOne = choice % 10 === 1; if (choicesLength < 4) { return (!teen && endsWithOne) ? 1 : 2; } if (!teen && endsWithOne) { return 1; } if (!teen && choice % 10 >= 2 && choice % 10 <= 4) { return 2; } return (choicesLength < 4) ? 2 : 3; } } }); ``` This would effectively give this: ```javascript const messages = { ru: { car: '0 машин | 1 машина | {n} машины | {n} машин', banana: 'нет бананов | 1 банан | {n} банана | {n} бананов' } } ``` Where the format is `0 things | 1 thing | few things | multiple things`. Your template still needs to use `$tc()`, not `$t()`: ```html

{{ $tc('car', 1) }}

{{ $tc('car', 2) }}

{{ $tc('car', 4) }}

{{ $tc('car', 12) }}

{{ $tc('car', 21) }}

{{ $tc('banana', 0) }}

{{ $tc('banana', 4) }}

{{ $tc('banana', 11) }}

{{ $tc('banana', 31) }}

``` Which results in: ```html

1 машина

2 машины

4 машины

12 машин

21 машина

нет бананов

4 банана

11 бананов

31 банан

``` ### Default pluralization If your current locale is not found in a pluralization map, the [default](#pluralization) rule of the english langugage will be used. ================================================ FILE: gitbook/en/sfc.md ================================================ # Single file components ## Basic Usage If you are building Vue component or Vue application with using single file components, you can manage the locale messages `i18n` custom block. The following in [single file components example](https://github.com/kazupon/vue-i18n/tree/dev/examples/sfc): ```html { "en": { "hello": "hello world!" }, "ja": { "hello": "こんにちは、世界!" } } ``` ## Installing vue-i18n-loader You need to install `vue-loader` and `vue-i18n-loader` due to use `` custom blocks. While [vue-loader](https://github.com/vuejs/vue-loader) most likely is already used in your project if you are working with single file components, you must install [vue-i18n-loader](https://github.com/kazupon/vue-i18n-loader) additionally: ``` $ npm i --save-dev @kazupon/vue-i18n-loader ``` ## Webpack For Webpack the configuration below is required: for vue-loader v15: ```js module.exports = { // ... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', }, { resourceQuery: /blockType=i18n/, loader: '@kazupon/vue-i18n-loader' } // ... ] }, // ... } ``` for vue-loader v14: ```js module.exports = { // ... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { // you need to specify `i18n` loaders key with `vue-i18n-loader` (https://github.com/kazupon/vue-i18n-loader) i18n: '@kazupon/vue-i18n-loader' } } }, // ... ] }, // ... } ``` ## Vue CLI 3.0 (beta) [Vue-cli 3.0](https://github.com/vuejs/vue-cli) hides the webpack configuration, so, if we want to add support to the `` tag inside a single file component we need to modify the existing configuration. In order to do that we have to create a `vue.config.js` at the root of our project. Once done that, we have to include the following: ```js const merge = require('deepmerge') module.exports = { chainWebpack: config => { config.module .rule('vue') .use('vue-loader') .tap(options => merge(options, { loaders: { i18n: '@kazupon/vue-i18n-loader' } }) ) } } ``` _Don't forget to install [deepmerge](https://github.com/KyleAMathews/deepmerge)! (`npm i deepmerge -D` or `yarn add deepmerge -D`)_ If you want to read more about modifying the existing configuration [click here](https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md). ## Laravel-Mix Laravel mix has its own rules for .vue files. To add the `vue-i18n-loader`, add the following to webpack.mix.js ```js mix.webpackConfig({ // ... module: { rules: [ { // Rules are copied from laravel-mix@1.5.1 /src/builder/webpack-rules.js and manually merged with the ia8n-loader. Make sure to update the rules to the latest found in webpack-rules.js test: /\.vue$/, loader: 'vue-loader', exclude: /bower_components/, options: { // extractCSS: Config.extractVueStyles, loaders: Config.extractVueStyles ? { js: { loader: 'babel-loader', options: Config.babel() }, scss: vueExtractPlugin.extract({ use: 'css-loader!sass-loader', fallback: 'vue-style-loader' }), sass: vueExtractPlugin.extract({ use: 'css-loader!sass-loader?indentedSyntax', fallback: 'vue-style-loader' }), css: vueExtractPlugin.extract({ use: 'css-loader', fallback: 'vue-style-loader' }), stylus: vueExtractPlugin.extract({ use: 'css-loader!stylus-loader?paths[]=node_modules', fallback: 'vue-style-loader' }), less: vueExtractPlugin.extract({ use: 'css-loader!less-loader', fallback: 'vue-style-loader' }), i18n: '@kazupon/vue-i18n-loader', } : { js: { loader: 'babel-loader', options: Config.babel() }, i18n: '@kazupon/vue-i18n-loader', }, postcss: Config.postCss, preLoaders: Config.vue.preLoaders, postLoaders: Config.vue.postLoaders, esModule: Config.vue.esModule } }, // ... ] }, // ... }); ``` ## YAML loading `i18n` custom blocks need to specify `JSON` format, also you can use `YAML` format by using pre-loader feature of `vue-loader`. the `i18n` custom blocks below of `YAML` format: ```html en: hello: "hello world!" ja: hello: "こんにちは、世界!" ``` Webpack conf the below: for vue-loader v15: ```js module.exports = { // ... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', }, { resourceQuery: /blockType=i18n/, use: [ {loader: '@kazupon/vue-i18n-loader'}, {loader: 'yaml-loader'} ] } // ... ] }, // ... } ``` for vue-loader v14: ```js module.exports = { // ... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { preLoaders: { i18n: 'yaml-loader' }, loaders: { i18n: '@kazupon/vue-i18n-loader' } } }, // ... ] }, // ... } ``` ## Multiple custom blocks you can be used the locale messages with multiple `i18n` custom block. ```html { "en": { "hello": "hello world!" }, "ja": { "hello": "こんにちは、世界!" } } ``` In the above, first custom block load the common locale message with `src` attribute, second custom block load the locale message that defined only at single file component. These locale messages will be merged as locale message of component. In this way, multiple custom blocks useful when want to be used as module. ================================================ FILE: gitbook/en/started.md ================================================ # Getting started > We will be using [ES2015](https://github.com/lukehoban/es6features) in the code samples in the guide. ### HTML ```html

{{ $t("message.hello") }}

``` ### JavaScript ```javascript // If using a module system (e.g. via vue-cli), import Vue and VueI18n and then call Vue.use(VueI18n). // import Vue from 'vue' // import VueI18n from 'vue-i18n' // // Vue.use(VueI18n) // Ready translated locale messages const messages = { en: { message: { hello: 'hello world' } }, ja: { message: { hello: 'こんにちは、世界' } } } // Create VueI18n instance with options const i18n = new VueI18n({ locale: 'ja', // set locale messages, // set locale messages }) // Create a Vue instance with `i18n` option new Vue({ i18n }).$mount('#app') // Now the app has started! ``` Output the following: ```html

こんにちは、世界

``` ================================================ FILE: gitbook/old/README.md ================================================ # vue-i18n documentation for v5.x Internationalization plugin of Vue.js * [Installation](https://github.com/kazupon/vue-i18n/tree/5.x/gitbook/installation.md) * [Getting Started](https://github.com/kazupon/vue-i18n/tree/5.x/gitbook/started.md) * [Formatting](https://github.com/kazupon/vue-i18n/tree/5.x/gitbook/formatting.md) * [Pluralization](https://github.com/kazupon/vue-i18n/tree/5.x/gitbook/pluralization.md) * [Locale and Keypath Syntax](https://github.com/kazupon/vue-i18n/tree/5.x/gitbook/syntax.md) * [Linked Translation](https://github.com/kazupon/vue-i18n/tree/5.x/gitbook/linked.md) * [Fallback Translation](https://github.com/kazupon/vue-i18n/tree/5.x/gitbook/fallback.md) * [Component Locale](https://github.com/kazupon/vue-i18n/tree/5.x/gitbook/component.md) * [Dynamic Locale](https://github.com/kazupon/vue-i18n/tree/5.x/gitbook/dynamic.md) * [Hot reload](https://github.com/kazupon/vue-i18n/tree/5.x/gitbook/hot-reload.md) * [API References](https://github.com/kazupon/vue-i18n/tree/5.x/gitbook/api.md) ================================================ FILE: package.json ================================================ { "name": "vue-i18n", "description": "Internationalization plugin for Vue.js", "version": "8.28.2", "author": { "name": "kazuya kawaguchi", "email": "kawakazu80@gmail.com" }, "bugs": { "url": "https://github.com/kazupon/vue-i18n/issues" }, "changelog": { "labels": { "Type: Feature": ":star: New Features", "Type: Bug": ":bug: Bug Fixes", "Type: Security": ":lock: Security Fixes", "Type: Performance": ":chart_with_upwards_trend: Performance Fixes", "Type: Improvement": ":zap: Improved Features", "Type: Breaking": ":boom: Breaking Change", "Type: Deprecated": ":warning: Deprecated Features", "Type: I18n": ":globe_with_meridians: Internationalization", "Type: A11y": ":wheelchair: Accessibility", "Type: Documentation": ":pencil: Documentation" } }, "devDependencies": { "@babel/core": "^7.1.0", "@babel/plugin-proposal-class-properties": "^7.1.0", "@babel/plugin-syntax-flow": "^7.0.0", "@babel/plugin-transform-flow-strip-types": "^7.0.0", "@typescript-eslint/eslint-plugin": "^3.0.0", "@typescript-eslint/parser": "^3.0.0", "@vue/babel-preset-app": "^4.4.1", "@vuepress/theme-vue": "^1.9.7", "babel-eslint": "^10.1.0", "babel-loader": "^8.1.0", "babel-plugin-istanbul": "^6.0.0", "babel-preset-power-assert": "^3.0.0", "buble": "^0.19.3", "chromedriver": "^127.0.2", "core-js": "^3.6.5", "cross-env": "^7.0.2", "cross-spawn": "^7.0.3", "eslint": "^6.8.0", "eslint-loader": "^4.0.2", "eslint-plugin-flowtype": "^4.7.0", "eslint-plugin-ie11": "^1.0.0", "eslint-plugin-no-autofix": "^1.0.1", "eslint-plugin-vue": "^7.14.0", "eslint-plugin-vue-libs": "^4.0.0", "flow-bin": "^0.38.0", "http-server": "^0.12.3", "intl": "^1.2.5", "karma": "^5.0.9", "karma-chrome-launcher": "^3.1.0", "karma-coverage": "^2.0.2", "karma-firefox-launcher": "^2.1.1", "karma-mocha": "^2.0.1", "karma-mocha-reporter": "^2.2.5", "karma-safari-launcher": "^1.0.0", "karma-sauce-launcher": "^4.1.5", "karma-sourcemap-loader": "^0.3.7", "karma-webpack": "^4.0.2", "lerna-changelog": "^1.0.0", "lerna-changelog-label-schema": "^3.0.0", "mocha": "^7.2.0", "mocha-loader": "^5.0.0", "nightwatch": "^1.3.5", "nightwatch-helpers": "^1.2.0", "power-assert": "^1.6.0", "rollup": "^0.66.0", "rollup-plugin-buble": "^0.19.2", "rollup-plugin-commonjs": "^9.1.8", "rollup-plugin-flow-no-whitespace": "^1.0.0", "rollup-plugin-node-resolve": "^3.4.0", "rollup-plugin-replace": "^2.0.0", "selenium-server": "^3.141.59", "shipjs": "^0.23.3", "sinon": "^11.1.1", "terser": "^3.17.0", "typescript": "^3.9.3", "vue": "^2.5.17", "vue-github-button": "^1.1.2", "vue-template-compiler": "^2.5.17", "vuepress": "^1.8.2", "webpack": "^4.43.0", "webpack-cli": "^4.7.2", "webpack-dev-middleware": "^5.0.0", "webpack-dev-server": "^3.11.0" }, "files": [ "dist/vue-i18n.js", "dist/vue-i18n.min.js", "dist/vue-i18n.common.js", "dist/vue-i18n.esm.js", "dist/vue-i18n.esm.browser.js", "dist/vue-i18n.esm.browser.min.js", "src/**/*.js", "types/*.d.ts", "decls", "vetur/tags.json", "vetur/attributes.json" ], "vetur": { "tags": "vetur/tags.json", "attributes": "vetur/attributes.json" }, "homepage": "https://github.com/kazupon/vue-i18n#readme", "keywords": [ "i18n", "internationalization", "plugin", "vue", "vue.js" ], "license": "MIT", "main": "dist/vue-i18n.common.js", "module": "dist/vue-i18n.esm.js", "repository": { "type": "git", "url": "git+https://github.com/kazupon/vue-i18n.git" }, "scripts": { "build": "node config/build.js", "clean": "rm -rf coverage && rm -rf dist/*.js* && rm ./*.log", "coverage": "cat ./coverage/lcov.info", "dev": "cross-env NODE_OPTIONS=--openssl-legacy-provider BABEL_ENV=test webpack-dev-server --inline --hot --open --content-base ./test/unit/ --config config/webpack.dev.conf.js", "docs:build": "cross-env NODE_OPTIONS=--openssl-legacy-provider NODE_ENV=production node config/version.js && cross-env NODE_ENV=production vuepress build vuepress -d docs", "docs:clean": "rm -rf docs/**", "docs:dev": "cross-env NODE_OPTIONS=--openssl-legacy-provider vuepress dev vuepress", "flow": "flow check", "lint": "eslint --fix src test types/**/*.ts", "release:prepare": "shipjs prepare", "release:trigger": "shipjs trigger", "sauce": "npm run sauce:coolkids && npm run sauce:ie && npm run sauce:mobile", "sauce:coolkids": "karma start config/karma.sauce.conf.js -- 0", "sauce:ie": "karma start config/karma.sauce.conf.js -- 1", "sauce:mobile": "karma start config/karma.sauce.conf.js -- 2", "test": "npm run lint && npm run flow && npm run test:types && npm run test:cover && npm run test:e2e", "test:cover": "cross-env NODE_OPTIONS=--openssl-legacy-provider BABEL_ENV=test karma start config/karma.cover.conf.js", "test:e2e": "cross-env NODE_OPTIONS=--openssl-legacy-provider node test/e2e/runner.js", "test:types": "tsc -p types", "test:unit": "cross-env NODE_OPTIONS=--openssl-legacy-provider BABEL_ENV=test karma start config/karma.unit.conf.js", "test:unit:ci": "cross-env NODE_OPTIONS=--openssl-legacy-provider BABEL_ENV=test karma start config/karma.unit.ci.conf.js" }, "sideEffects": false, "types": "types/index.d.ts", "unpkg": "dist/vue-i18n.js" } ================================================ FILE: scripts/docs-deploy.sh ================================================ #!/usr/bin/env sh # abort on errors set -e # build NODE_OPTIONS=--openssl-legacy-provider npm run docs:build # navigate into the build output directory cd docs # if you are deploying to a custom domain # echo 'www.example.com' > CNAME git init git add -A git commit -m 'deploy' git push -f git@github.com:kazupon/vue-i18n.git master:gh-pages cd - ================================================ FILE: scripts/remove.sh ================================================ #!/bin/bash set -xe rm -rf docs/assets/js/**.js rm -rf docs/assets/css/**.css rm -rf docs/pt/ rm -rf docs/patrons/RapidAPI.* rm -rf docs/patrons/babeledit.svg rm -rf docs/patrons/cypress.svg rm -rf docs/patrons/decibel.png rm -rf docs/patrons/localazy.svg rm -rf docs/patrons/nuxt.png rm -rf docs/patrons/plaid.svg rm -rf docs/patrons/storyblok.svg rm -rf docs/patrons/vuemastery.* rm -rf docs/patrons/crowdin.png rm -rf docs/patrons/crowdin.svg ================================================ FILE: ship.config.js ================================================ const execa = require(require.resolve('execa')) const { promisify } = require('util') const fs = require('fs') const path = require('path') const read = promisify(fs.readFile) const write = fs.writeFileSync function extractSpecificChangelog (changelog, version) { if (!changelog) { return null } const escapedVersion = version.replace(/\./g, '\\.') const regex = new RegExp( `(#+?\\s\\[?v?${escapedVersion}\\]?[\\s\\S]*?)(#+?\\s\\[?v?\\d+?\\.\\d+?\\.\\d+?\\]?)`, 'g' ) const matches = regex.exec(changelog) return matches ? matches[1] : null } async function commitChangelog (current, next) { const { stdout } = await execa('npx', ['lerna-changelog', '--next-version', `v${next}`]) const escapedVersion = next.replace(/\./g, '\\.') const regex = new RegExp( `(#+?\\s\\[?v?${escapedVersion}\\]?[\\s\\S]*?)(#+?\\s\\[?v?\\d\\.\\d\\.\\d\\]?)`, 'g' ) const matches = regex.exec(stdout.toString()) const head = matches ? matches[1] : stdout const changelog = await read('./CHANGELOG.md', 'utf8') return write('./CHANGELOG.md', `${head}\n\n${changelog}`) } module.exports = { mergeStrategy: { toSameBranch: ['v8.x'] }, monorepo: undefined, updateChangelog: false, beforeCommitChanges: ({ nextVersion, exec, dir }) => { return new Promise(resolve => { const pkg = require('./package.json') commitChangelog(pkg.version, nextVersion).then(resolve) }) }, formatCommitMessage: ({ version, releaseType, mergeStrategy, baseBranch }) => `${releaseType} release v${version}`, formatPullRequestTitle: ({ version, releaseType }) => `${releaseType} release v${version}`, shouldRelease: () => true, releases: { extractChangelog: ({ version, dir }) => { const changelogPath = path.resolve(dir, 'CHANGELOG.md') try { const changelogFile = fs.readFileSync(changelogPath, 'utf-8').toString() const ret = extractSpecificChangelog(changelogFile, version) return ret } catch (err) { if (err.code === 'ENOENT') { return null } throw err } } } } ================================================ FILE: src/components/interpolation.js ================================================ /* @flow */ import { warn } from '../util' export default { name: 'i18n', functional: true, props: { tag: { type: [String, Boolean, Object], default: 'span' }, path: { type: String, required: true }, locale: { type: String }, places: { type: [Array, Object] } }, render (h: Function, { data, parent, props, slots }: Object) { const { $i18n } = parent if (!$i18n) { if (process.env.NODE_ENV !== 'production') { warn('Cannot find VueI18n instance!') } return } const { path, locale, places } = props const params = slots() const children = $i18n.i( path, locale, onlyHasDefaultPlace(params) || places ? useLegacyPlaces(params.default, places) : params ) const tag = (!!props.tag && props.tag !== true) || props.tag === false ? props.tag : 'span' return tag ? h(tag, data, children) : children } } function onlyHasDefaultPlace (params) { let prop for (prop in params) { if (prop !== 'default') { return false } } return Boolean(prop) } function useLegacyPlaces (children, places) { const params = places ? createParamsFromPlaces(places) : {} if (!children) { return params } // Filter empty text nodes children = children.filter(child => { return child.tag || child.text.trim() !== '' }) const everyPlace = children.every(vnodeHasPlaceAttribute) if (process.env.NODE_ENV !== 'production' && everyPlace) { warn('`place` attribute is deprecated in next major version. Please switch to Vue slots.') } return children.reduce( everyPlace ? assignChildPlace : assignChildIndex, params ) } function createParamsFromPlaces (places) { if (process.env.NODE_ENV !== 'production') { warn('`places` prop is deprecated in next major version. Please switch to Vue slots.') } return Array.isArray(places) ? places.reduce(assignChildIndex, {}) : Object.assign({}, places) } function assignChildPlace (params, child) { if (child.data && child.data.attrs && child.data.attrs.place) { params[child.data.attrs.place] = child } return params } function assignChildIndex (params, child, index) { params[index] = child return params } function vnodeHasPlaceAttribute (vnode) { return Boolean(vnode.data && vnode.data.attrs && vnode.data.attrs.place) } ================================================ FILE: src/components/number.js ================================================ /* @flow */ import { warn, isString, isObject, includes, numberFormatKeys } from '../util' export default { name: 'i18n-n', functional: true, props: { tag: { type: [String, Boolean, Object], default: 'span' }, value: { type: Number, required: true }, format: { type: [String, Object] }, locale: { type: String } }, render (h: Function, { props, parent, data }: Object) { const i18n = parent.$i18n if (!i18n) { if (process.env.NODE_ENV !== 'production') { warn('Cannot find VueI18n instance!') } return null } let key: ?string = null let options: ?NumberFormatOptions = null if (isString(props.format)) { key = props.format } else if (isObject(props.format)) { if (props.format.key) { key = props.format.key } // Filter out number format options only options = Object.keys(props.format).reduce((acc, prop) => { if (includes(numberFormatKeys, prop)) { return Object.assign({}, acc, { [prop]: props.format[prop] }) } return acc }, null) } const locale: Locale = props.locale || i18n.locale const parts: NumberFormatToPartsResult = i18n._ntp(props.value, locale, key, options) const values = parts.map((part, index) => { const slot: ?Function = data.scopedSlots && data.scopedSlots[part.type] return slot ? slot({ [part.type]: part.value, index, parts }) : part.value }) const tag = (!!props.tag && props.tag !== true) || props.tag === false ? props.tag : 'span' return tag ? h(tag, { attrs: data.attrs, 'class': data['class'], staticClass: data.staticClass }, values) : values } } ================================================ FILE: src/directive.js ================================================ /* @flow */ import { warn, isString, isPlainObject, looseEqual } from './util' export function bind (el: any, binding: Object, vnode: any): void { if (!assert(el, vnode)) { return } t(el, binding, vnode) } export function update (el: any, binding: Object, vnode: any, oldVNode: any): void { if (!assert(el, vnode)) { return } const i18n: any = vnode.context.$i18n if (localeEqual(el, vnode) && (looseEqual(binding.value, binding.oldValue) && looseEqual(el._localeMessage, i18n.getLocaleMessage(i18n.locale)))) { return } t(el, binding, vnode) } export function unbind (el: any, binding: Object, vnode: any, oldVNode: any): void { const vm: any = vnode.context if (!vm) { warn('Vue instance does not exists in VNode context') return } const i18n: any = vnode.context.$i18n || {} if (!binding.modifiers.preserve && !i18n.preserveDirectiveContent) { el.textContent = '' } el._vt = undefined delete el['_vt'] el._locale = undefined delete el['_locale'] el._localeMessage = undefined delete el['_localeMessage'] } function assert (el: any, vnode: any): boolean { const vm: any = vnode.context if (!vm) { warn('Vue instance does not exists in VNode context') return false } if (!vm.$i18n) { warn('VueI18n instance does not exists in Vue instance') return false } return true } function localeEqual (el: any, vnode: any): boolean { const vm: any = vnode.context return el._locale === vm.$i18n.locale } function t (el: any, binding: Object, vnode: any): void { const value: any = binding.value const { path, locale, args, choice } = parseValue(value) if (!path && !locale && !args) { warn('value type not supported') return } if (!path) { warn('`path` is required in v-t directive') return } const vm: any = vnode.context if (choice != null) { el._vt = el.textContent = vm.$i18n.tc(path, choice, ...makeParams(locale, args)) } else { el._vt = el.textContent = vm.$i18n.t(path, ...makeParams(locale, args)) } el._locale = vm.$i18n.locale el._localeMessage = vm.$i18n.getLocaleMessage(vm.$i18n.locale) } function parseValue (value: any): Object { let path: ?string let locale: ?Locale let args: any let choice: ?number if (isString(value)) { path = value } else if (isPlainObject(value)) { path = value.path locale = value.locale args = value.args choice = value.choice } return { path, locale, args, choice } } function makeParams (locale: Locale, args: any): Array { const params: Array = [] locale && params.push(locale) if (args && (Array.isArray(args) || isPlainObject(args))) { params.push(args) } return params } ================================================ FILE: src/extend.js ================================================ /* @flow */ export default function extend (Vue: any): void { if (!Vue.prototype.hasOwnProperty('$i18n')) { // $FlowFixMe Object.defineProperty(Vue.prototype, '$i18n', { get () { return this._i18n } }) } Vue.prototype.$t = function (key: Path, ...values: any): TranslateResult { const i18n = this.$i18n return i18n._t(key, i18n.locale, i18n._getMessages(), this, ...values) } Vue.prototype.$tc = function (key: Path, choice?: number, ...values: any): TranslateResult { const i18n = this.$i18n return i18n._tc(key, i18n.locale, i18n._getMessages(), this, choice, ...values) } Vue.prototype.$te = function (key: Path, locale?: Locale): boolean { const i18n = this.$i18n return i18n._te(key, i18n.locale, i18n._getMessages(), locale) } Vue.prototype.$d = function (value: number | Date, ...args: any): DateTimeFormatResult { return this.$i18n.d(value, ...args) } Vue.prototype.$n = function (value: number, ...args: any): NumberFormatResult { return this.$i18n.n(value, ...args) } } ================================================ FILE: src/format.js ================================================ /* @flow */ import { warn, isObject } from './util' export default class BaseFormatter { _caches: { [key: string]: Array } constructor () { this._caches = Object.create(null) } interpolate (message: string, values: any): Array { if (!values) { return [message] } let tokens: Array = this._caches[message] if (!tokens) { tokens = parse(message) this._caches[message] = tokens } return compile(tokens, values) } } type Token = { type: 'text' | 'named' | 'list' | 'unknown', value: string } const RE_TOKEN_LIST_VALUE: RegExp = /^(?:\d)+/ const RE_TOKEN_NAMED_VALUE: RegExp = /^(?:\w)+/ export function parse (format: string): Array { const tokens: Array = [] let position: number = 0 let text: string = '' while (position < format.length) { let char: string = format[position++] if (char === '{') { if (text) { tokens.push({ type: 'text', value: text }) } text = '' let sub: string = '' char = format[position++] while (char !== undefined && char !== '}') { sub += char char = format[position++] } const isClosed = char === '}' const type = RE_TOKEN_LIST_VALUE.test(sub) ? 'list' : isClosed && RE_TOKEN_NAMED_VALUE.test(sub) ? 'named' : 'unknown' tokens.push({ value: sub, type }) } else if (char === '%') { // when found rails i18n syntax, skip text capture if (format[(position)] !== '{') { text += char } } else { text += char } } text && tokens.push({ type: 'text', value: text }) return tokens } export function compile (tokens: Array, values: Object | Array): Array { const compiled: Array = [] let index: number = 0 const mode: string = Array.isArray(values) ? 'list' : isObject(values) ? 'named' : 'unknown' if (mode === 'unknown') { return compiled } while (index < tokens.length) { const token: Token = tokens[index] switch (token.type) { case 'text': compiled.push(token.value) break case 'list': compiled.push(values[parseInt(token.value, 10)]) break case 'named': if (mode === 'named') { compiled.push((values: any)[token.value]) } else { if (process.env.NODE_ENV !== 'production') { warn(`Type of token '${token.type}' and format of value '${mode}' don't match!`) } } break case 'unknown': if (process.env.NODE_ENV !== 'production') { warn(`Detect 'unknown' type of token!`) } break } index++ } return compiled } ================================================ FILE: src/index.js ================================================ /* @flow */ import { install, Vue } from './install' import { warn, error, isNull, parseArgs, isPlainObject, isObject, isArray, isBoolean, isString, isFunction, looseClone, remove, arrayFrom, includes, merge, numberFormatKeys, dateTimeFormatKeys, escapeParams } from './util' import BaseFormatter from './format' import I18nPath from './path' import type { PathValue } from './path' const htmlTagMatcher = /<\/?[\w\s="/.':;#-\/]+>/ const linkKeyMatcher = /(?:@(?:\.[a-zA-Z]+)?:(?:[\w\-_|./]+|\([\w\-_:|./]+\)))/g const linkKeyPrefixMatcher = /^@(?:\.([a-zA-Z]+))?:/ const bracketsMatcher = /[()]/g const defaultModifiers = { 'upper': str => str.toLocaleUpperCase(), 'lower': str => str.toLocaleLowerCase(), 'capitalize': str => `${str.charAt(0).toLocaleUpperCase()}${str.substr(1)}` } const defaultFormatter = new BaseFormatter() export default class VueI18n { static install: () => void static version: string static availabilities: IntlAvailability _vm: any _formatter: Formatter _modifiers: Modifiers _root: any _sync: boolean _fallbackRoot: boolean _fallbackRootWithEmptyString: boolean _localeChainCache: { [key: string]: Array; } _missing: ?MissingHandler _exist: Function _silentTranslationWarn: boolean | RegExp _silentFallbackWarn: boolean | RegExp _formatFallbackMessages: boolean _dateTimeFormatters: Object _numberFormatters: Object _path: I18nPath _dataListeners: Set _componentInstanceCreatedListener: ?ComponentInstanceCreatedListener _preserveDirectiveContent: boolean _warnHtmlInMessage: WarnHtmlInMessageLevel _escapeParameterHtml: boolean _postTranslation: ?PostTranslationHandler __VUE_I18N_BRIDGE__: ?string pluralizationRules: { [lang: string]: (choice: number, choicesLength: number) => number } getChoiceIndex: GetChoiceIndex constructor (options: I18nOptions = {}) { // Auto install if it is not done yet and `window` has `Vue`. // To allow users to avoid auto-installation in some cases, // this code should be placed here. See #290 /* istanbul ignore if */ if (!Vue && typeof window !== 'undefined' && window.Vue) { install(window.Vue) } const locale: Locale = options.locale || 'en-US' const fallbackLocale: FallbackLocale = options.fallbackLocale === false ? false : options.fallbackLocale || 'en-US' const messages: LocaleMessages = options.messages || {} const dateTimeFormats = options.dateTimeFormats || options.datetimeFormats || {} const numberFormats = options.numberFormats || {} this._vm = null this._formatter = options.formatter || defaultFormatter this._modifiers = options.modifiers || {} this._missing = options.missing || null this._root = options.root || null this._sync = options.sync === undefined ? true : !!options.sync this._fallbackRoot = options.fallbackRoot === undefined ? true : !!options.fallbackRoot this._fallbackRootWithEmptyString = options.fallbackRootWithEmptyString === undefined ? true : !!options.fallbackRootWithEmptyString this._formatFallbackMessages = options.formatFallbackMessages === undefined ? false : !!options.formatFallbackMessages this._silentTranslationWarn = options.silentTranslationWarn === undefined ? false : options.silentTranslationWarn this._silentFallbackWarn = options.silentFallbackWarn === undefined ? false : !!options.silentFallbackWarn this._dateTimeFormatters = {} this._numberFormatters = {} this._path = new I18nPath() this._dataListeners = new Set() this._componentInstanceCreatedListener = options.componentInstanceCreatedListener || null this._preserveDirectiveContent = options.preserveDirectiveContent === undefined ? false : !!options.preserveDirectiveContent this.pluralizationRules = options.pluralizationRules || {} this._warnHtmlInMessage = options.warnHtmlInMessage || 'off' this._postTranslation = options.postTranslation || null this._escapeParameterHtml = options.escapeParameterHtml || false if ('__VUE_I18N_BRIDGE__' in options) { this.__VUE_I18N_BRIDGE__ = options.__VUE_I18N_BRIDGE__ } /** * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)` * @param choicesLength {number} an overall amount of available choices * @returns a final choice index */ this.getChoiceIndex = (choice: number, choicesLength: number): number => { const thisPrototype = Object.getPrototypeOf(this) if (thisPrototype && thisPrototype.getChoiceIndex) { const prototypeGetChoiceIndex = (thisPrototype.getChoiceIndex: any) return (prototypeGetChoiceIndex: GetChoiceIndex).call(this, choice, choicesLength) } // Default (old) getChoiceIndex implementation - english-compatible const defaultImpl = (_choice: number, _choicesLength: number) => { _choice = Math.abs(_choice) if (_choicesLength === 2) { return _choice ? _choice > 1 ? 1 : 0 : 1 } return _choice ? Math.min(_choice, 2) : 0 } if (this.locale in this.pluralizationRules) { return this.pluralizationRules[this.locale].apply(this, [choice, choicesLength]) } else { return defaultImpl(choice, choicesLength) } } this._exist = (message: Object, key: Path): boolean => { if (!message || !key) { return false } if (!isNull(this._path.getPathValue(message, key))) { return true } // fallback for flat key if (message[key]) { return true } return false } if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') { Object.keys(messages).forEach(locale => { this._checkLocaleMessage(locale, this._warnHtmlInMessage, messages[locale]) }) } this._initVM({ locale, fallbackLocale, messages, dateTimeFormats, numberFormats }) } _checkLocaleMessage (locale: Locale, level: WarnHtmlInMessageLevel, message: LocaleMessageObject): void { const paths: Array = [] const fn = (level: WarnHtmlInMessageLevel, locale: Locale, message: any, paths: Array) => { if (isPlainObject(message)) { Object.keys(message).forEach(key => { const val = message[key] if (isPlainObject(val)) { paths.push(key) paths.push('.') fn(level, locale, val, paths) paths.pop() paths.pop() } else { paths.push(key) fn(level, locale, val, paths) paths.pop() } }) } else if (isArray(message)) { message.forEach((item, index) => { if (isPlainObject(item)) { paths.push(`[${index}]`) paths.push('.') fn(level, locale, item, paths) paths.pop() paths.pop() } else { paths.push(`[${index}]`) fn(level, locale, item, paths) paths.pop() } }) } else if (isString(message)) { const ret = htmlTagMatcher.test(message) if (ret) { const msg = `Detected HTML in message '${message}' of keypath '${paths.join('')}' at '${locale}'. Consider component interpolation with '' to avoid XSS. See https://bit.ly/2ZqJzkp` if (level === 'warn') { warn(msg) } else if (level === 'error') { error(msg) } } } } fn(level, locale, message, paths) } _initVM (data: { locale: Locale, fallbackLocale: FallbackLocale, messages: LocaleMessages, dateTimeFormats: DateTimeFormats, numberFormats: NumberFormats }): void { const silent = Vue.config.silent Vue.config.silent = true this._vm = new Vue({ data, __VUE18N__INSTANCE__: true }) Vue.config.silent = silent } destroyVM (): void { this._vm.$destroy() } subscribeDataChanging (vm: any): void { this._dataListeners.add(vm) } unsubscribeDataChanging (vm: any): void { remove(this._dataListeners, vm) } watchI18nData (): Function { const self = this return this._vm.$watch('$data', () => { const listeners = arrayFrom(this._dataListeners) let i = listeners.length while(i--) { Vue.nextTick(() => { listeners[i] && listeners[i].$forceUpdate() }) } }, { deep: true }) } watchLocale (composer?: any): ?Function { if (!composer) { /* istanbul ignore if */ if (!this._sync || !this._root) { return null } const target: any = this._vm return this._root.$i18n.vm.$watch('locale', (val) => { target.$set(target, 'locale', val) target.$forceUpdate() }, { immediate: true }) } else { // deal with vue-i18n-bridge if (!this.__VUE_I18N_BRIDGE__) { return null } const self = this const target: any = this._vm return this.vm.$watch('locale', (val) => { target.$set(target, 'locale', val) if (self.__VUE_I18N_BRIDGE__ && composer) { composer.locale.value = val } target.$forceUpdate() }, { immediate: true }) } } onComponentInstanceCreated (newI18n: I18n) { if (this._componentInstanceCreatedListener) { this._componentInstanceCreatedListener(newI18n, this) } } get vm (): any { return this._vm } get messages (): LocaleMessages { return looseClone(this._getMessages()) } get dateTimeFormats (): DateTimeFormats { return looseClone(this._getDateTimeFormats()) } get numberFormats (): NumberFormats { return looseClone(this._getNumberFormats()) } get availableLocales (): Locale[] { return Object.keys(this.messages).sort() } get locale (): Locale { return this._vm.locale } set locale (locale: Locale): void { this._vm.$set(this._vm, 'locale', locale) } get fallbackLocale (): FallbackLocale { return this._vm.fallbackLocale } set fallbackLocale (locale: FallbackLocale): void { this._localeChainCache = {} this._vm.$set(this._vm, 'fallbackLocale', locale) } get formatFallbackMessages (): boolean { return this._formatFallbackMessages } set formatFallbackMessages (fallback: boolean): void { this._formatFallbackMessages = fallback } get missing (): ?MissingHandler { return this._missing } set missing (handler: MissingHandler): void { this._missing = handler } get formatter (): Formatter { return this._formatter } set formatter (formatter: Formatter): void { this._formatter = formatter } get silentTranslationWarn (): boolean | RegExp { return this._silentTranslationWarn } set silentTranslationWarn (silent: boolean | RegExp): void { this._silentTranslationWarn = silent } get silentFallbackWarn (): boolean | RegExp { return this._silentFallbackWarn } set silentFallbackWarn (silent: boolean | RegExp): void { this._silentFallbackWarn = silent } get preserveDirectiveContent (): boolean { return this._preserveDirectiveContent } set preserveDirectiveContent (preserve: boolean): void { this._preserveDirectiveContent = preserve } get warnHtmlInMessage (): WarnHtmlInMessageLevel { return this._warnHtmlInMessage } set warnHtmlInMessage (level: WarnHtmlInMessageLevel): void { const orgLevel = this._warnHtmlInMessage this._warnHtmlInMessage = level if (orgLevel !== level && (level === 'warn' || level === 'error')) { const messages = this._getMessages() Object.keys(messages).forEach(locale => { this._checkLocaleMessage(locale, this._warnHtmlInMessage, messages[locale]) }) } } get postTranslation (): ?PostTranslationHandler { return this._postTranslation } set postTranslation (handler: PostTranslationHandler): void { this._postTranslation = handler } get sync (): boolean { return this._sync } set sync (val: boolean): void { this._sync = val } _getMessages (): LocaleMessages { return this._vm.messages } _getDateTimeFormats (): DateTimeFormats { return this._vm.dateTimeFormats } _getNumberFormats (): NumberFormats { return this._vm.numberFormats } _warnDefault (locale: Locale, key: Path, result: ?any, vm: ?any, values: any, interpolateMode: string): ?string { if (!isNull(result)) { return result } if (this._missing) { const missingRet = this._missing.apply(null, [locale, key, vm, values]) if (isString(missingRet)) { return missingRet } } else { if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key)) { warn( `Cannot translate the value of keypath '${key}'. ` + 'Use the value of keypath as default.' ) } } if (this._formatFallbackMessages) { const parsedArgs = parseArgs(...values) return this._render(key, interpolateMode, parsedArgs.params, key) } else { return key } } _isFallbackRoot (val: any): boolean { return (this._fallbackRootWithEmptyString? !val : isNull(val)) && !isNull(this._root) && this._fallbackRoot } _isSilentFallbackWarn (key: Path): boolean { return this._silentFallbackWarn instanceof RegExp ? this._silentFallbackWarn.test(key) : this._silentFallbackWarn } _isSilentFallback (locale: Locale, key: Path): boolean { return this._isSilentFallbackWarn(key) && (this._isFallbackRoot() || locale !== this.fallbackLocale) } _isSilentTranslationWarn (key: Path): boolean { return this._silentTranslationWarn instanceof RegExp ? this._silentTranslationWarn.test(key) : this._silentTranslationWarn } _interpolate ( locale: Locale, message: LocaleMessageObject, key: Path, host: any, interpolateMode: string, values: any, visitedLinkStack: Array ): any { if (!message) { return null } const pathRet: PathValue = this._path.getPathValue(message, key) if (isArray(pathRet) || isPlainObject(pathRet)) { return pathRet } let ret: mixed if (isNull(pathRet)) { /* istanbul ignore else */ if (isPlainObject(message)) { ret = message[key] if (!(isString(ret) || isFunction(ret))) { if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallback(locale, key)) { warn(`Value of key '${key}' is not a string or function !`) } return null } } else { return null } } else { /* istanbul ignore else */ if (isString(pathRet) || isFunction(pathRet)) { ret = pathRet } else { if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallback(locale, key)) { warn(`Value of key '${key}' is not a string or function!`) } return null } } // Check for the existence of links within the translated string if (isString(ret) && (ret.indexOf('@:') >= 0 || ret.indexOf('@.') >= 0)) { ret = this._link(locale, message, ret, host, 'raw', values, visitedLinkStack) } return this._render(ret, interpolateMode, values, key) } _link ( locale: Locale, message: LocaleMessageObject, str: string, host: any, interpolateMode: string, values: any, visitedLinkStack: Array ): any { let ret: string = str // Match all the links within the local // We are going to replace each of // them with its translation const matches: any = ret.match(linkKeyMatcher) // eslint-disable-next-line no-autofix/prefer-const for (let idx in matches) { // ie compatible: filter custom array // prototype method if (!matches.hasOwnProperty(idx)) { continue } const link: string = matches[idx] const linkKeyPrefixMatches: any = link.match(linkKeyPrefixMatcher) const [linkPrefix, formatterName] = linkKeyPrefixMatches // Remove the leading @:, @.case: and the brackets const linkPlaceholder: string = link.replace(linkPrefix, '').replace(bracketsMatcher, '') if (includes(visitedLinkStack, linkPlaceholder)) { if (process.env.NODE_ENV !== 'production') { warn(`Circular reference found. "${link}" is already visited in the chain of ${visitedLinkStack.reverse().join(' <- ')}`) } return ret } visitedLinkStack.push(linkPlaceholder) // Translate the link let translated: any = this._interpolate( locale, message, linkPlaceholder, host, interpolateMode === 'raw' ? 'string' : interpolateMode, interpolateMode === 'raw' ? undefined : values, visitedLinkStack ) if (this._isFallbackRoot(translated)) { if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(linkPlaceholder)) { warn(`Fall back to translate the link placeholder '${linkPlaceholder}' with root locale.`) } /* istanbul ignore if */ if (!this._root) { throw Error('unexpected error') } const root: any = this._root.$i18n translated = root._translate( root._getMessages(), root.locale, root.fallbackLocale, linkPlaceholder, host, interpolateMode, values ) } translated = this._warnDefault( locale, linkPlaceholder, translated, host, isArray(values) ? values : [values], interpolateMode ) if (this._modifiers.hasOwnProperty(formatterName)) { translated = this._modifiers[formatterName](translated) } else if (defaultModifiers.hasOwnProperty(formatterName)) { translated = defaultModifiers[formatterName](translated) } visitedLinkStack.pop() // Replace the link with the translated ret = !translated ? ret : ret.replace(link, translated) } return ret } _createMessageContext (values: any, formatter: Formatter, path: string, interpolateMode: string): MessageContext { const _list = isArray(values) ? values : [] const _named = isObject(values) ? values : {} const list = (index: number): mixed => _list[index] const named = (key: string): mixed => _named[key] const messages = this._getMessages() const locale = this.locale return { list, named, values, formatter, path, messages, locale, linked: (linkedKey: string) => this._interpolate(locale, messages[locale] || {}, linkedKey, null, interpolateMode, undefined, [linkedKey]) } } _render (message: string | MessageFunction, interpolateMode: string, values: any, path: string): any { if (isFunction(message)) { return message( this._createMessageContext(values, this._formatter || defaultFormatter, path, interpolateMode) ) } let ret = this._formatter.interpolate(message, values, path) // If the custom formatter refuses to work - apply the default one if (!ret) { ret = defaultFormatter.interpolate(message, values, path) } // if interpolateMode is **not** 'string' ('row'), // return the compiled data (e.g. ['foo', VNode, 'bar']) with formatter return interpolateMode === 'string' && !isString(ret) ? ret.join('') : ret } _appendItemToChain (chain: Array, item: Locale, blocks: any): any { let follow = false if (!includes(chain, item)) { follow = true if (item) { follow = item[item.length - 1] !== '!' item = item.replace(/!/g, '') chain.push(item) if (blocks && blocks[item]) { follow = blocks[item] } } } return follow } _appendLocaleToChain (chain: Array, locale: Locale, blocks: any): any { let follow const tokens = locale.split('-') do { const item = tokens.join('-') follow = this._appendItemToChain(chain, item, blocks) tokens.splice(-1, 1) } while (tokens.length && (follow === true)) return follow } _appendBlockToChain (chain: Array, block: Array | Object, blocks: any): any { let follow = true for (let i = 0; (i < block.length) && (isBoolean(follow)); i++) { const locale = block[i] if (isString(locale)) { follow = this._appendLocaleToChain(chain, locale, blocks) } } return follow } _getLocaleChain (start: Locale, fallbackLocale: FallbackLocale): Array { if (start === '') { return [] } if (!this._localeChainCache) { this._localeChainCache = {} } let chain = this._localeChainCache[start] if (!chain) { if (!fallbackLocale) { fallbackLocale = this.fallbackLocale } chain = [] // first block defined by start let block = [start] // while any intervening block found while (isArray(block)) { block = this._appendBlockToChain( chain, block, fallbackLocale ) } // last block defined by default let defaults if (isArray(fallbackLocale)) { defaults = fallbackLocale } else if (isObject(fallbackLocale)) { /* $FlowFixMe */ if (fallbackLocale['default']) { defaults = fallbackLocale['default'] } else { defaults = null } } else { defaults = fallbackLocale } // convert defaults to array if (isString(defaults)) { block = [defaults] } else { block = defaults } if (block) { this._appendBlockToChain( chain, block, null ) } this._localeChainCache[start] = chain } return chain } _translate ( messages: LocaleMessages, locale: Locale, fallback: FallbackLocale, key: Path, host: any, interpolateMode: string, args: any ): any { const chain = this._getLocaleChain(locale, fallback) let res for (let i = 0; i < chain.length; i++) { const step = chain[i] res = this._interpolate(step, messages[step], key, host, interpolateMode, args, [key]) if (!isNull(res)) { if (step !== locale && process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) { warn(("Fall back to translate the keypath '" + key + "' with '" + step + "' locale.")) } return res } } return null } _t (key: Path, _locale: Locale, messages: LocaleMessages, host: any, ...values: any): any { if (!key) { return '' } const parsedArgs = parseArgs(...values) if(this._escapeParameterHtml) { parsedArgs.params = escapeParams(parsedArgs.params) } const locale: Locale = parsedArgs.locale || _locale let ret: any = this._translate( messages, locale, this.fallbackLocale, key, host, 'string', parsedArgs.params ) if (this._isFallbackRoot(ret)) { if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) { warn(`Fall back to translate the keypath '${key}' with root locale.`) } /* istanbul ignore if */ if (!this._root) { throw Error('unexpected error') } return this._root.$t(key, ...values) } else { ret = this._warnDefault(locale, key, ret, host, values, 'string') if (this._postTranslation && ret !== null && ret !== undefined) { ret = this._postTranslation(ret, key) } return ret } } t (key: Path, ...values: any): TranslateResult { return this._t(key, this.locale, this._getMessages(), null, ...values) } _i (key: Path, locale: Locale, messages: LocaleMessages, host: any, values: Object): any { const ret: any = this._translate(messages, locale, this.fallbackLocale, key, host, 'raw', values) if (this._isFallbackRoot(ret)) { if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key)) { warn(`Fall back to interpolate the keypath '${key}' with root locale.`) } if (!this._root) { throw Error('unexpected error') } return this._root.$i18n.i(key, locale, values) } else { return this._warnDefault(locale, key, ret, host, [values], 'raw') } } i (key: Path, locale: Locale, values: Object): TranslateResult { /* istanbul ignore if */ if (!key) { return '' } if (!isString(locale)) { locale = this.locale } return this._i(key, locale, this._getMessages(), null, values) } _tc ( key: Path, _locale: Locale, messages: LocaleMessages, host: any, choice?: number, ...values: any ): any { if (!key) { return '' } if (choice === undefined) { choice = 1 } const predefined = { 'count': choice, 'n': choice } const parsedArgs = parseArgs(...values) parsedArgs.params = Object.assign(predefined, parsedArgs.params) values = parsedArgs.locale === null ? [parsedArgs.params] : [parsedArgs.locale, parsedArgs.params] return this.fetchChoice(this._t(key, _locale, messages, host, ...values), choice) } fetchChoice (message: string, choice: number): ?string { /* istanbul ignore if */ if (!message || !isString(message)) { return null } const choices: Array = message.split('|') choice = this.getChoiceIndex(choice, choices.length) if (!choices[choice]) { return message } return choices[choice].trim() } tc (key: Path, choice?: number, ...values: any): TranslateResult { return this._tc(key, this.locale, this._getMessages(), null, choice, ...values) } _te (key: Path, locale: Locale, messages: LocaleMessages, ...args: any): boolean { const _locale: Locale = parseArgs(...args).locale || locale return this._exist(messages[_locale], key) } te (key: Path, locale?: Locale): boolean { return this._te(key, this.locale, this._getMessages(), locale) } getLocaleMessage (locale: Locale): LocaleMessageObject { return looseClone(this._vm.messages[locale] || {}) } setLocaleMessage (locale: Locale, message: LocaleMessageObject): void { if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') { this._checkLocaleMessage(locale, this._warnHtmlInMessage, message) } this._vm.$set(this._vm.messages, locale, message) } mergeLocaleMessage (locale: Locale, message: LocaleMessageObject): void { if (this._warnHtmlInMessage === 'warn' || this._warnHtmlInMessage === 'error') { this._checkLocaleMessage(locale, this._warnHtmlInMessage, message) } this._vm.$set(this._vm.messages, locale, merge( typeof this._vm.messages[locale] !== 'undefined' && Object.keys(this._vm.messages[locale]).length ? Object.assign({}, this._vm.messages[locale]) : {}, message )) } getDateTimeFormat (locale: Locale): DateTimeFormat { return looseClone(this._vm.dateTimeFormats[locale] || {}) } setDateTimeFormat (locale: Locale, format: DateTimeFormat): void { this._vm.$set(this._vm.dateTimeFormats, locale, format) this._clearDateTimeFormat(locale, format) } mergeDateTimeFormat (locale: Locale, format: DateTimeFormat): void { this._vm.$set(this._vm.dateTimeFormats, locale, merge(this._vm.dateTimeFormats[locale] || {}, format)) this._clearDateTimeFormat(locale, format) } _clearDateTimeFormat (locale: Locale, format: DateTimeFormat): void { // eslint-disable-next-line no-autofix/prefer-const for (let key in format) { const id = `${locale}__${key}` if (!this._dateTimeFormatters.hasOwnProperty(id)) { continue } delete this._dateTimeFormatters[id] } } _localizeDateTime ( value: number | Date, locale: Locale, fallback: FallbackLocale, dateTimeFormats: DateTimeFormats, key: string, options: ?DateTimeFormatOptions ): ?DateTimeFormatResult { let _locale: Locale = locale let formats: DateTimeFormat = dateTimeFormats[_locale] const chain = this._getLocaleChain(locale, fallback) for (let i = 0; i < chain.length; i++) { const current = _locale const step = chain[i] formats = dateTimeFormats[step] _locale = step // fallback locale if (isNull(formats) || isNull(formats[key])) { if (step !== locale && process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) { warn(`Fall back to '${step}' datetime formats from '${current}' datetime formats.`) } } else { break } } if (isNull(formats) || isNull(formats[key])) { return null } else { const format: ?DateTimeFormatOptions = formats[key] let formatter if (options) { formatter = new Intl.DateTimeFormat(_locale, Object.assign({}, format, options)) } else { const id = `${_locale}__${key}` formatter = this._dateTimeFormatters[id] if (!formatter) { formatter = this._dateTimeFormatters[id] = new Intl.DateTimeFormat(_locale, format) } } return formatter.format(value) } } _d (value: number | Date, locale: Locale, key: ?string, options: ?DateTimeFormatOptions): DateTimeFormatResult { /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && !VueI18n.availabilities.dateTimeFormat) { warn('Cannot format a Date value due to not supported Intl.DateTimeFormat.') return '' } if (!key) { const dtf = !options ? new Intl.DateTimeFormat(locale) : new Intl.DateTimeFormat(locale, options) return dtf.format(value) } const ret: ?DateTimeFormatResult = this._localizeDateTime(value, locale, this.fallbackLocale, this._getDateTimeFormats(), key, options) if (this._isFallbackRoot(ret)) { if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) { warn(`Fall back to datetime localization of root: key '${key}'.`) } /* istanbul ignore if */ if (!this._root) { throw Error('unexpected error') } return this._root.$i18n.d(value, key, locale) } else { return ret || '' } } d (value: number | Date, ...args: any): DateTimeFormatResult { let locale: Locale = this.locale let key: ?string = null let options: ?DateTimeFormatOptions = null if (args.length === 1) { if (isString(args[0])) { key = args[0] } else if (isObject(args[0])) { if (args[0].locale) { locale = args[0].locale } if (args[0].key) { key = args[0].key } } options = Object.keys(args[0]).reduce((acc, key) => { if (includes(dateTimeFormatKeys, key)) { return Object.assign({}, acc, { [key]: args[0][key] }) } return acc }, null) } else if (args.length === 2) { if (isString(args[0])) { key = args[0] } if (isString(args[1])) { locale = args[1] } } return this._d(value, locale, key, options) } getNumberFormat (locale: Locale): NumberFormat { return looseClone(this._vm.numberFormats[locale] || {}) } setNumberFormat (locale: Locale, format: NumberFormat): void { this._vm.$set(this._vm.numberFormats, locale, format) this._clearNumberFormat(locale, format) } mergeNumberFormat (locale: Locale, format: NumberFormat): void { this._vm.$set(this._vm.numberFormats, locale, merge(this._vm.numberFormats[locale] || {}, format)) this._clearNumberFormat(locale, format) } _clearNumberFormat (locale: Locale, format: NumberFormat): void { // eslint-disable-next-line no-autofix/prefer-const for (let key in format) { const id = `${locale}__${key}` if (!this._numberFormatters.hasOwnProperty(id)) { continue } delete this._numberFormatters[id] } } _getNumberFormatter ( value: number, locale: Locale, fallback: FallbackLocale, numberFormats: NumberFormats, key: string, options: ?NumberFormatOptions ): ?Object { let _locale: Locale = locale let formats: NumberFormat = numberFormats[_locale] const chain = this._getLocaleChain(locale, fallback) for (let i = 0; i < chain.length; i++) { const current = _locale const step = chain[i] formats = numberFormats[step] _locale = step // fallback locale if (isNull(formats) || isNull(formats[key])) { if (step !== locale && process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) { warn(`Fall back to '${step}' number formats from '${current}' number formats.`) } } else { break } } if (isNull(formats) || isNull(formats[key])) { return null } else { const format: ?NumberFormatOptions = formats[key] let formatter if (options) { // If options specified - create one time number formatter formatter = new Intl.NumberFormat(_locale, Object.assign({}, format, options)) } else { const id = `${_locale}__${key}` formatter = this._numberFormatters[id] if (!formatter) { formatter = this._numberFormatters[id] = new Intl.NumberFormat(_locale, format) } } return formatter } } _n (value: number, locale: Locale, key: ?string, options: ?NumberFormatOptions): NumberFormatResult { /* istanbul ignore if */ if (!VueI18n.availabilities.numberFormat) { if (process.env.NODE_ENV !== 'production') { warn('Cannot format a Number value due to not supported Intl.NumberFormat.') } return '' } if (!key) { const nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options) return nf.format(value) } const formatter: ?Object = this._getNumberFormatter(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options) const ret: ?NumberFormatResult = formatter && formatter.format(value) if (this._isFallbackRoot(ret)) { if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key) && !this._isSilentFallbackWarn(key)) { warn(`Fall back to number localization of root: key '${key}'.`) } /* istanbul ignore if */ if (!this._root) { throw Error('unexpected error') } return this._root.$i18n.n(value, Object.assign({}, { key, locale }, options)) } else { return ret || '' } } n (value: number, ...args: any): NumberFormatResult { let locale: Locale = this.locale let key: ?string = null let options: ?NumberFormatOptions = null if (args.length === 1) { if (isString(args[0])) { key = args[0] } else if (isObject(args[0])) { if (args[0].locale) { locale = args[0].locale } if (args[0].key) { key = args[0].key } // Filter out number format options only options = Object.keys(args[0]).reduce((acc, key) => { if (includes(numberFormatKeys, key)) { return Object.assign({}, acc, { [key]: args[0][key] }) } return acc }, null) } } else if (args.length === 2) { if (isString(args[0])) { key = args[0] } if (isString(args[1])) { locale = args[1] } } return this._n(value, locale, key, options) } _ntp (value: number, locale: Locale, key: ?string, options: ?NumberFormatOptions): NumberFormatToPartsResult { /* istanbul ignore if */ if (!VueI18n.availabilities.numberFormat) { if (process.env.NODE_ENV !== 'production') { warn('Cannot format to parts a Number value due to not supported Intl.NumberFormat.') } return [] } if (!key) { const nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options) return nf.formatToParts(value) } const formatter: ?Object = this._getNumberFormatter(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options) const ret: ?NumberFormatToPartsResult = formatter && formatter.formatToParts(value) if (this._isFallbackRoot(ret)) { if (process.env.NODE_ENV !== 'production' && !this._isSilentTranslationWarn(key)) { warn(`Fall back to format number to parts of root: key '${key}' .`) } /* istanbul ignore if */ if (!this._root) { throw Error('unexpected error') } return this._root.$i18n._ntp(value, locale, key, options) } else { return ret || [] } } } let availabilities: IntlAvailability // $FlowFixMe Object.defineProperty(VueI18n, 'availabilities', { get () { if (!availabilities) { const intlDefined = typeof Intl !== 'undefined' availabilities = { dateTimeFormat: intlDefined && typeof Intl.DateTimeFormat !== 'undefined', numberFormat: intlDefined && typeof Intl.NumberFormat !== 'undefined' } } return availabilities } }) VueI18n.install = install VueI18n.version = '__VERSION__' ================================================ FILE: src/install.js ================================================ import { warn } from './util' import extend from './extend' import defineMixin from './mixin' import interpolationComponent from './components/interpolation' import numberComponent from './components/number' import { bind, update, unbind } from './directive' export let Vue export function install (_Vue, options = { bridge: false }) { /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && install.installed && _Vue === Vue) { warn('already installed.') return } install.installed = true Vue = _Vue const version = (Vue.version && Number(Vue.version.split('.')[0])) || -1 /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && version < 2) { warn(`vue-i18n (${install.version}) need to use Vue 2.0 or later (Vue: ${Vue.version}).`) return } extend(Vue) Vue.mixin(defineMixin(options.bridge)) Vue.directive('t', { bind, update, unbind }) Vue.component(interpolationComponent.name, interpolationComponent) Vue.component(numberComponent.name, numberComponent) // use simple mergeStrategies to prevent i18n instance lose '__proto__' const strats = Vue.config.optionMergeStrategies strats.i18n = function (parentVal, childVal) { return childVal === undefined ? parentVal : childVal } } ================================================ FILE: src/mixin.js ================================================ /* @flow */ import VueI18n from './index' import { isPlainObject, warn, error, merge } from './util' /** * Mixin * * If `bridge` mode, empty mixin is returned, * else regulary mixin implementation is returned. */ export default function defineMixin (bridge: boolean = false) { function mounted (): void { if (this !== this.$root && this.$options.__INTLIFY_META__ && this.$el) { this.$el.setAttribute('data-intlify', this.$options.__INTLIFY_META__) } } return bridge ? { mounted } // delegate `vue-i18n-bridge` mixin implementation : { // regulary beforeCreate (): void { const options: any = this.$options options.i18n = options.i18n || ((options.__i18nBridge || options.__i18n) ? {} : null) if (options.i18n) { if (options.i18n instanceof VueI18n) { // init locale messages via custom blocks if ((options.__i18nBridge || options.__i18n)) { try { let localeMessages = options.i18n && options.i18n.messages ? options.i18n.messages : {} const __i18n = options.__i18nBridge || options.__i18n __i18n.forEach(resource => { localeMessages = merge(localeMessages, JSON.parse(resource)) }) Object.keys(localeMessages).forEach((locale: Locale) => { options.i18n.mergeLocaleMessage(locale, localeMessages[locale]) }) } catch (e) { if (process.env.NODE_ENV !== 'production') { error(`Cannot parse locale messages via custom blocks.`, e) } } } this._i18n = options.i18n this._i18nWatcher = this._i18n.watchI18nData() } else if (isPlainObject(options.i18n)) { const rootI18n = this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n ? this.$root.$i18n : null // component local i18n if (rootI18n) { options.i18n.root = this.$root options.i18n.formatter = rootI18n.formatter options.i18n.fallbackLocale = rootI18n.fallbackLocale options.i18n.formatFallbackMessages = rootI18n.formatFallbackMessages options.i18n.silentTranslationWarn = rootI18n.silentTranslationWarn options.i18n.silentFallbackWarn = rootI18n.silentFallbackWarn options.i18n.pluralizationRules = rootI18n.pluralizationRules options.i18n.preserveDirectiveContent = rootI18n.preserveDirectiveContent } // init locale messages via custom blocks if ((options.__i18nBridge || options.__i18n)) { try { let localeMessages = options.i18n && options.i18n.messages ? options.i18n.messages : {} const __i18n = options.__i18nBridge || options.__i18n __i18n.forEach(resource => { localeMessages = merge(localeMessages, JSON.parse(resource)) }) options.i18n.messages = localeMessages } catch (e) { if (process.env.NODE_ENV !== 'production') { warn(`Cannot parse locale messages via custom blocks.`, e) } } } const { sharedMessages } = options.i18n if (sharedMessages && isPlainObject(sharedMessages)) { options.i18n.messages = merge(options.i18n.messages, sharedMessages) } this._i18n = new VueI18n(options.i18n) this._i18nWatcher = this._i18n.watchI18nData() if (options.i18n.sync === undefined || !!options.i18n.sync) { this._localeWatcher = this.$i18n.watchLocale() } if (rootI18n) { rootI18n.onComponentInstanceCreated(this._i18n) } } else { if (process.env.NODE_ENV !== 'production') { warn(`Cannot be interpreted 'i18n' option.`) } } } else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) { // root i18n this._i18n = this.$root.$i18n } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) { // parent i18n this._i18n = options.parent.$i18n } }, beforeMount (): void { const options: any = this.$options options.i18n = options.i18n || ((options.__i18nBridge || options.__i18n) ? {} : null) if (options.i18n) { if (options.i18n instanceof VueI18n) { // init locale messages via custom blocks this._i18n.subscribeDataChanging(this) this._subscribing = true } else if (isPlainObject(options.i18n)) { this._i18n.subscribeDataChanging(this) this._subscribing = true } else { if (process.env.NODE_ENV !== 'production') { warn(`Cannot be interpreted 'i18n' option.`) } } } else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) { this._i18n.subscribeDataChanging(this) this._subscribing = true } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) { this._i18n.subscribeDataChanging(this) this._subscribing = true } }, mounted, beforeDestroy (): void { if (!this._i18n) { return } const self = this this.$nextTick(() => { if (self._subscribing) { self._i18n.unsubscribeDataChanging(self) delete self._subscribing } if (self._i18nWatcher) { self._i18nWatcher() self._i18n.destroyVM() delete self._i18nWatcher } if (self._localeWatcher) { self._localeWatcher() delete self._localeWatcher } }) } } } ================================================ FILE: src/path.js ================================================ /* @flow */ import { isObject } from './util' /** * Path parser * - Inspired: * Vue.js Path parser */ // actions const APPEND = 0 const PUSH = 1 const INC_SUB_PATH_DEPTH = 2 const PUSH_SUB_PATH = 3 // states const BEFORE_PATH = 0 const IN_PATH = 1 const BEFORE_IDENT = 2 const IN_IDENT = 3 const IN_SUB_PATH = 4 const IN_SINGLE_QUOTE = 5 const IN_DOUBLE_QUOTE = 6 const AFTER_PATH = 7 const ERROR = 8 const pathStateMachine: any = [] pathStateMachine[BEFORE_PATH] = { 'ws': [BEFORE_PATH], 'ident': [IN_IDENT, APPEND], '[': [IN_SUB_PATH], 'eof': [AFTER_PATH] } pathStateMachine[IN_PATH] = { 'ws': [IN_PATH], '.': [BEFORE_IDENT], '[': [IN_SUB_PATH], 'eof': [AFTER_PATH] } pathStateMachine[BEFORE_IDENT] = { 'ws': [BEFORE_IDENT], 'ident': [IN_IDENT, APPEND], '0': [IN_IDENT, APPEND], 'number': [IN_IDENT, APPEND] } pathStateMachine[IN_IDENT] = { 'ident': [IN_IDENT, APPEND], '0': [IN_IDENT, APPEND], 'number': [IN_IDENT, APPEND], 'ws': [IN_PATH, PUSH], '.': [BEFORE_IDENT, PUSH], '[': [IN_SUB_PATH, PUSH], 'eof': [AFTER_PATH, PUSH] } pathStateMachine[IN_SUB_PATH] = { "'": [IN_SINGLE_QUOTE, APPEND], '"': [IN_DOUBLE_QUOTE, APPEND], '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH], ']': [IN_PATH, PUSH_SUB_PATH], 'eof': ERROR, 'else': [IN_SUB_PATH, APPEND] } pathStateMachine[IN_SINGLE_QUOTE] = { "'": [IN_SUB_PATH, APPEND], 'eof': ERROR, 'else': [IN_SINGLE_QUOTE, APPEND] } pathStateMachine[IN_DOUBLE_QUOTE] = { '"': [IN_SUB_PATH, APPEND], 'eof': ERROR, 'else': [IN_DOUBLE_QUOTE, APPEND] } /** * Check if an expression is a literal value. */ const literalValueRE: RegExp = /^\s?(?:true|false|-?[\d.]+|'[^']*'|"[^"]*")\s?$/ function isLiteral (exp: string): boolean { return literalValueRE.test(exp) } /** * Strip quotes from a string */ function stripQuotes (str: string): string | boolean { const a: number = str.charCodeAt(0) const b: number = str.charCodeAt(str.length - 1) return a === b && (a === 0x22 || a === 0x27) ? str.slice(1, -1) : str } /** * Determine the type of a character in a keypath. */ function getPathCharType (ch: ?string): string { if (ch === undefined || ch === null) { return 'eof' } const code: number = ch.charCodeAt(0) switch (code) { case 0x5B: // [ case 0x5D: // ] case 0x2E: // . case 0x22: // " case 0x27: // ' return ch case 0x5F: // _ case 0x24: // $ case 0x2D: // - return 'ident' case 0x09: // Tab case 0x0A: // Newline case 0x0D: // Return case 0xA0: // No-break space case 0xFEFF: // Byte Order Mark case 0x2028: // Line Separator case 0x2029: // Paragraph Separator return 'ws' } return 'ident' } /** * Format a subPath, return its plain form if it is * a literal string or number. Otherwise prepend the * dynamic indicator (*). */ function formatSubPath (path: string): boolean | string { const trimmed: string = path.trim() // invalid leading 0 if (path.charAt(0) === '0' && isNaN(path)) { return false } return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed } /** * Parse a string path into an array of segments */ function parse (path: Path): ?Array { const keys: Array = [] let index: number = -1 let mode: number = BEFORE_PATH let subPathDepth: number = 0 let c: ?string let key: any let newChar: any let type: string let transition: number let action: Function let typeMap: any const actions: Array = [] actions[PUSH] = function () { if (key !== undefined) { keys.push(key) key = undefined } } actions[APPEND] = function () { if (key === undefined) { key = newChar } else { key += newChar } } actions[INC_SUB_PATH_DEPTH] = function () { actions[APPEND]() subPathDepth++ } actions[PUSH_SUB_PATH] = function () { if (subPathDepth > 0) { subPathDepth-- mode = IN_SUB_PATH actions[APPEND]() } else { subPathDepth = 0 if (key === undefined) { return false } key = formatSubPath(key) if (key === false) { return false } else { actions[PUSH]() } } } function maybeUnescapeQuote (): ?boolean { const nextChar: string = path[index + 1] if ((mode === IN_SINGLE_QUOTE && nextChar === "'") || (mode === IN_DOUBLE_QUOTE && nextChar === '"')) { index++ newChar = '\\' + nextChar actions[APPEND]() return true } } while (mode !== null) { index++ c = path[index] if (c === '\\' && maybeUnescapeQuote()) { continue } type = getPathCharType(c) typeMap = pathStateMachine[mode] transition = typeMap[type] || typeMap['else'] || ERROR if (transition === ERROR) { return // parse error } mode = transition[0] action = actions[transition[1]] if (action) { newChar = transition[2] newChar = newChar === undefined ? c : newChar if (action() === false) { return } } if (mode === AFTER_PATH) { return keys } } } export type PathValue = PathValueObject | PathValueArray | Function | string | number | boolean | null export type PathValueObject = { [key: string]: PathValue } export type PathValueArray = Array export default class I18nPath { _cache: Object constructor () { this._cache = Object.create(null) } /** * External parse that check for a cache hit first */ parsePath (path: Path): Array { let hit: ?Array = this._cache[path] if (!hit) { hit = parse(path) if (hit) { this._cache[path] = hit } } return hit || [] } /** * Get path value from path string */ getPathValue (obj: mixed, path: Path): PathValue { if (!isObject(obj)) { return null } const paths: Array = this.parsePath(path) if (paths.length === 0) { return null } else { const length: number = paths.length let last: any = obj let i: number = 0 while (i < length) { const value: any = last[paths[i]] if (value === undefined || value === null) { return null } last = value i++ } return last } } } ================================================ FILE: src/util.js ================================================ /* @flow */ /** * constants */ export const numberFormatKeys = [ 'compactDisplay', 'currency', 'currencyDisplay', 'currencySign', 'localeMatcher', 'notation', 'numberingSystem', 'signDisplay', 'style', 'unit', 'unitDisplay', 'useGrouping', 'minimumIntegerDigits', 'minimumFractionDigits', 'maximumFractionDigits', 'minimumSignificantDigits', 'maximumSignificantDigits' ] export const dateTimeFormatKeys = [ 'dateStyle', 'timeStyle', 'calendar', 'localeMatcher', "hour12", "hourCycle", "timeZone", "formatMatcher", 'weekday', 'era', 'year', 'month', 'day', 'hour', 'minute', 'second', 'timeZoneName', ] /** * utilities */ export function warn (msg: string, err: ?Error): void { if (typeof console !== 'undefined') { console.warn('[vue-i18n] ' + msg) /* istanbul ignore if */ if (err) { console.warn(err.stack) } } } export function error (msg: string, err: ?Error): void { if (typeof console !== 'undefined') { console.error('[vue-i18n] ' + msg) /* istanbul ignore if */ if (err) { console.error(err.stack) } } } export const isArray = Array.isArray export function isObject (obj: mixed): boolean %checks { return obj !== null && typeof obj === 'object' } export function isBoolean (val: mixed): boolean %checks { return typeof val === 'boolean' } export function isString (val: mixed): boolean %checks { return typeof val === 'string' } const toString: Function = Object.prototype.toString const OBJECT_STRING: string = '[object Object]' export function isPlainObject (obj: any): boolean { return toString.call(obj) === OBJECT_STRING } export function isNull (val: mixed): boolean { return val === null || val === undefined } export function isFunction (val: mixed): boolean %checks { return typeof val === 'function' } export function parseArgs (...args: Array): Object { let locale: ?string = null let params: mixed = null if (args.length === 1) { if (isObject(args[0]) || isArray(args[0])) { params = args[0] } else if (typeof args[0] === 'string') { locale = args[0] } } else if (args.length === 2) { if (typeof args[0] === 'string') { locale = args[0] } /* istanbul ignore if */ if (isObject(args[1]) || isArray(args[1])) { params = args[1] } } return { locale, params } } export function looseClone (obj: Object): Object { return JSON.parse(JSON.stringify(obj)) } export function remove (arr: Set, item: any): Set | void { if (arr.delete(item)) { return arr } } export function arrayFrom (arr: Set): Array { const ret = [] arr.forEach(a => ret.push(a)) return ret } export function includes (arr: Array, item: any): boolean { return !!~arr.indexOf(item) } const hasOwnProperty = Object.prototype.hasOwnProperty export function hasOwn (obj: Object | Array<*>, key: string): boolean { return hasOwnProperty.call(obj, key) } export function merge (target: Object): Object { const output = Object(target) for (let i = 1; i < arguments.length; i++) { const source = arguments[i] if (source !== undefined && source !== null) { let key for (key in source) { if (hasOwn(source, key)) { if (isObject(source[key])) { output[key] = merge(output[key], source[key]) } else { output[key] = source[key] } } } } } return output } export function looseEqual (a: any, b: any): boolean { if (a === b) { return true } const isObjectA: boolean = isObject(a) const isObjectB: boolean = isObject(b) if (isObjectA && isObjectB) { try { const isArrayA: boolean = isArray(a) const isArrayB: boolean = isArray(b) if (isArrayA && isArrayB) { return a.length === b.length && a.every((e: any, i: number): boolean => { return looseEqual(e, b[i]) }) } else if (!isArrayA && !isArrayB) { const keysA: Array = Object.keys(a) const keysB: Array = Object.keys(b) return keysA.length === keysB.length && keysA.every((key: string): boolean => { return looseEqual(a[key], b[key]) }) } else { /* istanbul ignore next */ return false } } catch (e) { /* istanbul ignore next */ return false } } else if (!isObjectA && !isObjectB) { return String(a) === String(b) } else { return false } } /** * Sanitizes html special characters from input strings. For mitigating risk of XSS attacks. * @param rawText The raw input from the user that should be escaped. */ function escapeHtml(rawText: string): string { return rawText .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, ''') } /** * Escapes html tags and special symbols from all provided params which were returned from parseArgs().params. * This method performs an in-place operation on the params object. * * @param {any} params Parameters as provided from `parseArgs().params`. * May be either an array of strings or a string->any map. * * @returns The manipulated `params` object. */ export function escapeParams(params: any): any { if(params != null) { Object.keys(params).forEach(key => { if(typeof(params[key]) == 'string') { params[key] = escapeHtml(params[key]) } }) } return params } ================================================ FILE: test/.eslintrc.js ================================================ module.exports = { env: { browser: true, mocha: true }, globals: { isChrome: true, assert: true, sinon: true, Vue: true, VueI18n: true }, extends: [ "plugin:vue/recommended" ] } ================================================ FILE: test/e2e/runner.js ================================================ 'use strict' const path = require('path') const spawn = require('cross-spawn') const httpServer = require('http-server') const server = httpServer.createServer({ root: path.resolve(__dirname, '../../') }) server.listen(8080) let args = process.argv.slice(2) if (args.indexOf('--config') === -1) { args = args.concat(['--config', 'config/nightwatch.conf.js']) } if (args.indexOf('--env') === -1) { if (process.env.CI) { args = args.concat(['--env', 'headless']) } else { args = args.concat(['--env', 'chrome,headless']) } } const i = args.indexOf('--test') if (i > -1) { args[i + 1] = 'test/e2e/test/' + args[i + 1] } const runner = spawn('./node_modules/.bin/nightwatch', args, { stdio: 'inherit' }) runner.on('exit', code => { server.close() process.exit(code) }) runner.on('error', err => { server.close() throw err }) ================================================ FILE: test/e2e/test/component.js ================================================ module.exports = { component: function (browser) { browser .url('http://localhost:8080/examples/component/') .waitForElementVisible('#app', 1000) .assert.containsText('p:nth-child(1)', 'こんにちは、世界') .assert.containsText('div.container p:nth-child(1)', 'Component1 locale messages: こんにちは、component1') .assert.containsText('div.container p:nth-child(2)', 'Fallback global locale messages: おはよう、世界!') .end() } } ================================================ FILE: test/e2e/test/datetime.js ================================================ module.exports = { number: function (browser) { browser .url('http://localhost:8080/examples/datetime/') .waitForElementVisible('#app', 1000) .assert.containsText('p[data-testid="pastDatetime"]', '過去の日時: 2012/12/20 午後00:00:00') .assert.containsText('p[data-testid="pastYear"]', '過去の年: 12年') .click('select option[value=en-US]') .assert.containsText('p[data-testid="pastDatetime"]', 'past datetime: 12/19/2012, 07:00:00 PM') .assert.containsText('p[data-testid="pastYear"]', 'past year: 12') .end() } } ================================================ FILE: test/e2e/test/directive.js ================================================ module.exports = { started: function (browser) { browser .url('http://localhost:8080/examples/directive/') .waitForElementVisible('#string-syntax', 1000) .waitForElementVisible('#object-syntax', 1000) .assert.containsText('#string-syntax p:nth-child(1)', 'hi there!') .assert.containsText('#string-syntax p:nth-child(2)', 'hi there!') .assert.containsText('#object-syntax p:nth-child(1)', 'こんにちは、kazupon!') .assert.containsText('#object-syntax p:nth-child(2)', 'hi kazupon!') .end() } } ================================================ FILE: test/e2e/test/formatting_html.js ================================================ module.exports = { 'formatting html': function (browser) { browser .url('http://localhost:8080/examples/formatting/html/') .waitForElementVisible('#app', 1000) .assert.attributeContains('p', 'innerHTML', '') .end() } } ================================================ FILE: test/e2e/test/formatting_list.js ================================================ module.exports = { 'formatting list': function (browser) { browser .url('http://localhost:8080/examples/formatting/list/') .waitForElementVisible('#app', 1000) .assert.containsText('p', 'hello world') .end() } } ================================================ FILE: test/e2e/test/formatting_named.js ================================================ module.exports = { 'formatting name': function (browser) { browser .url('http://localhost:8080/examples/formatting/named/') .waitForElementVisible('#app', 1000) .assert.containsText('p', 'hello world') .end() } } ================================================ FILE: test/e2e/test/formatting_rails.js ================================================ module.exports = { 'formatting rails': function (browser) { browser .url('http://localhost:8080/examples/formatting/rails/') .waitForElementVisible('#app', 1000) .assert.containsText('p', 'hello world') .end() } } ================================================ FILE: test/e2e/test/interpolation.js ================================================ module.exports = { interpolation: function (browser) { browser .url('http://localhost:8080/examples/interpolation/slots') .waitForElementVisible('#app', 1000) .assert.containsText('label[for="tos"]', 'I accept xxx Term of Service.') .end() } } ================================================ FILE: test/e2e/test/number.js ================================================ module.exports = { number: function (browser) { browser .url('http://localhost:8080/examples/number/') .waitForElementVisible('#app', 1000) .assert.containsText('p', 'お金: ¥1,000') .click('select option[value=en-US]') .assert.containsText('p', 'Money: $1,000') .end() } } ================================================ FILE: test/e2e/test/number_formatting.js ================================================ module.exports = { 'number_formatting': function (browser) { browser .url('http://localhost:8080/examples/number-formatting/') .waitForElementVisible('#app', 1000) .assert.containsText('p.money', '¥1,000') .click('select option[value=en-US]') .assert.attributeContains('p.money', 'innerHTML', '$1,00000') .end() } } ================================================ FILE: test/e2e/test/started.js ================================================ module.exports = { started: function (browser) { browser .url('http://localhost:8080/examples/started/') .waitForElementVisible('p', 1000) .assert.containsText('p', 'こんにちは、世界') .end() } } ================================================ FILE: test/helpers/entry.js ================================================ import Vue from 'vue' import VueI18n from '../../src/index' Vue.use(VueI18n) window.VueI18n = VueI18n window.Vue = Vue ================================================ FILE: test/helpers/imports.js ================================================ import assert from 'assert' import sinon from 'sinon' window.assert = assert window.sinon = sinon ================================================ FILE: test/helpers/util.js ================================================ const inBrowser = typeof window !== 'undefined' const UA = inBrowser && window.navigator.userAgent.toLowerCase() window.isChrome = UA && UA.indexOf('chrome') !== -1 function getVersion () { const ua = window.navigator.userAgent let tem let M = ua.match(/(phantomjs|opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [] if (/trident/i.test(M[1])) { tem = /\brv[ :]+(\d+)/g.exec(ua) || [] return { name: 'IE', version: parseInt((tem[1] || '-1'), 10) } } if (M[1] === 'Chrome') { tem = ua.match(/\bOPR|Edge\/(\d+)/) if (tem !== null) { return { name: 'Opera', version: parseInt(tem[1], 10) } } } M = M[2] ? [M[1], M[2]] : [window.navigator.appName, window.navigator.appVersion, '-?'] if ((tem = ua.match(/version\/(\d+)/i)) !== null) { M.splice(1, 1, tem[1]) } return { name: M[0], version: parseInt(M[1], 10) } } const { browser, version } = getVersion() window.availableIntl = (browser === 'IE' && version >= 11) || (browser === 'Firefox' && version >= 29) || (browser === 'Chrome' && version >= 24) || (browser === 'Safari' && version >= 10) ================================================ FILE: test/unit/basic.test.js ================================================ import messages from './fixture/index' import dateTimeFormats from './fixture/datetime' import numberFormats from './fixture/number' describe('basic', () => { let i18n beforeEach(() => { i18n = new VueI18n({ locale: 'en', fallbackLocale: 'en', messages, modifiers: { custom: str => str.replace(/[aeiou]/g, 'x') } }) }) describe('i18n#t', () => { describe('en locale', () => { it('should translate an english', () => { assert.strictEqual(i18n.t('message.hello'), messages.en.message.hello) }) }) describe('empty string', () => { it('should support empty string', () => { assert.strictEqual(i18n.t('message.empty'), messages.en.message.empty) }) }) describe('linked translation', () => { it('should translate simple link', () => { assert.strictEqual(i18n.t('message.link'), messages.en.message.hello) }) it('should translate link at the end of locale', () => { assert.strictEqual(i18n.t('message.linkEnd'), 'This is a linked translation to the world') }) it('should translate link within a locale', () => { assert.strictEqual(i18n.t('message.linkWithin'), 'Isn\'t the world we live in great?') }) it('should translate multiple links within a locale', () => { assert.strictEqual(i18n.t('message.linkMultiple'), 'Hello hoge!, isn\'t the world great?') }) it('should translate link with braces ', () => { assert.strictEqual(i18n.t('message.linkBrackets'), 'Hello hoge. Isn\'t the world great?') }) it('should translate link with lower-case formatting', () => { assert.strictEqual(i18n.t('message.linkCaseLower'), 'Please provide home address') }) it('should translate link with upper-case formatting', () => { assert.strictEqual(i18n.t('message.linkCaseUpper'), 'HOME ADDRESS') }) it('should translate link with the first letter as upper-case', () => { assert.strictEqual(i18n.t('message.linkCaseCapitalize'), 'Home Address') }) it('should translate link without formatting if modifier is not known.', () => { assert.strictEqual(i18n.t('message.linkCaseUnknown'), 'home Address') }) it('should render link with custom formatting.', () => { assert.strictEqual(i18n.t('message.linkCaseCustom'), 'hxmx Addrxss') }) it('should translate link when keys have special characters', () => { assert.strictEqual(i18n.t('message.linkHyphen'), messages.en['hyphen-hello']) assert.strictEqual(i18n.t('message.linkUnderscore'), messages.en.underscore_hello) assert.strictEqual(i18n.t('message.linkColon'), messages.en['colon:hello']) assert.strictEqual(i18n.t('message.linkPipe'), messages.en['pipe|hello']) }) }) describe('ja locale', () => { it('should translate a japanese', () => { assert.strictEqual(i18n.t('message.hello', 'ja'), messages.ja.message.hello) }) }) describe('key argument', () => { describe('not specify', () => { it('should return empty string', () => { assert.strictEqual(i18n.t(), '') }) }) describe('empty string', () => { it('should return empty string', () => { assert.strictEqual(i18n.t(''), '') }) }) describe('not regist key', () => { it('should return key string', () => { assert.strictEqual(i18n.t('foo.bar'), 'foo.bar') }) }) describe('sentence fragment', () => { it('should translate fragment', () => { assert.strictEqual(i18n.t('hello world'), 'Hello World') }) it('should return replaced string if available', () => { assert.strictEqual( i18n.t('Hello {0}', ['kazupon']), 'Hello kazupon' ) }) it('should return key if unavailable', () => { assert.strictEqual(i18n.t('Hello'), 'Hello') }) }) describe('object keypath', () => { it('should be translated', () => { assert.strictEqual(i18n.t('message.format'), messages.en.message.format) }) it('should be translated if keypath contains spaces', () => { assert.strictEqual( i18n.t('message.Hello {0}', ['kazupon']), 'Hello kazupon' ) }) }) describe('array keypath', () => { describe('basic', () => { it('should be translated', () => { assert.strictEqual(i18n.t('errors[0]'), messages.en.errors[0]) }) }) describe('object attribute', () => { it('should be translated', () => { assert.strictEqual(i18n.t('errors[1].internal1'), messages.en.errors[1].internal1) }) }) describe('object', () => { it('should be translated', () => { assert.strictEqual(i18n.t('errors[1]'), messages.en.errors[1]) }) }) describe('array', () => { it('should be translated', () => { assert.strictEqual(i18n.t('errors[2][0]'), messages.en.errors[2][0]) }) }) }) }) describe('format arguments', () => { describe('named', () => { it('should return replaced string', () => { assert.strictEqual( i18n.t('message.format.named', { name: 'kazupon' }), 'Hello kazupon, how are you?' ) }) }) describe('list', () => { it('should return replaced string', () => { assert.strictEqual( i18n.t('message.format.list', ['kazupon']), 'Hello kazupon, how are you?' ) }) }) }) describe('format arguments of fallback', () => { describe('if activated', () => { describe('named', () => { it('should return replaced string', () => { i18n = new VueI18n({ locale: 'en', fallbackLocale: 'en', formatFallbackMessages: true }) assert.strictEqual( i18n.t('Hello {name}, how are you?', { name: 'kazupon' }), 'Hello kazupon, how are you?' ) }) }) describe('list', () => { it('should return replaced string', () => { i18n = new VueI18n({ locale: 'en', fallbackLocale: 'en', formatFallbackMessages: true }) assert.strictEqual( i18n.t('Hello {0}, how are you?', ['kazupon']), 'Hello kazupon, how are you?' ) }) }) }) describe('if not activated', () => { describe('named', () => { it('should not return replaced string', () => { assert.strictEqual( i18n.t('Hello {name}, how are you?', { name: 'kazupon' }), 'Hello {name}, how are you?' ) }) }) describe('list', () => { it('should not return replaced string', () => { assert.strictEqual( i18n.t('Hello {0}, how are you?', ['kazupon']), 'Hello {0}, how are you?' ) }) }) }) }) describe('locale argument', () => { it('should return empty string', () => { assert.strictEqual(i18n.t('message.hello', 'ja'), messages.ja.message.hello) }) }) describe('format & locale arguments', () => { it('should return replaced string', () => { assert.strictEqual( i18n.t('message.format.list', 'ja', ['kazupon']), 'こんにちは kazupon, ごきげんいかが?' ) }) }) describe('fallback', () => { it('should return fallback string', () => { assert.strictEqual( i18n.t('message.fallback', 'ja'), messages.en.message.fallback ) }) }) }) describe('i18n#tc', () => { describe('default choice', () => { it('should be choice singluar', () => { assert.strictEqual(i18n.tc('plurals.apple'), 'one apple') }) }) describe('split plural with zero choice', () => { it('should allow a zero choice, a one choice and a plural choice', () => { const count = 10 assert.strictEqual(i18n.tc('plurals.apple', 0), 'no apples') assert.strictEqual(i18n.tc('plurals.apple', 1), 'one apple') assert.strictEqual(i18n.tc('plurals.apple', count, { count }), '10 apples') }) }) describe('implicit choice exposing', () => { describe('en locale', () => { it('should expose "count" implicitly to locale message', () => { assert.strictEqual(i18n.tc('plurals.apple', 10), '10 apples') }) it('should not expose if given explicitly', () => { const explicitArgs = { 'count': 'Many' } assert.strictEqual(i18n.tc('plurals.apple', 10, explicitArgs), 'Many apples') }) }) describe('ja locale', () => { it('should expose "count" and "n" implicitly to locale message', () => { assert.strictEqual(i18n.tc('plurals.implicitPluralCount', 10, 'ja'), 'count:10, n:10') }) it('should not expose if given explicitly', () => { const explicitArgs = { 'count': 'たくさん', 'n': '大量' } assert.strictEqual(i18n.tc('plurals.implicitPluralCount', 10, 'ja', explicitArgs), 'count:たくさん, n:大量') }) }) }) describe('en locale', () => { it('should translate an english', () => { assert.strictEqual(i18n.tc('plurals.car', 1), 'car') }) }) describe('multi plural check', () => { it('should fetch pluralized string', () => { assert.strictEqual(i18n.tc('plurals.car', 2), 'cars') }) }) describe('ja locale', () => { it('should translate a japanese', () => { assert.strictEqual(i18n.tc('plurals.car', 1, 'ja'), 'ザ・ワールド') }) }) describe('key argument', () => { describe('not specify', () => { it('should return empty string', () => { assert.strictEqual(i18n.tc(), '') }) }) describe('empty string', () => { it('should return empty string', () => { assert.strictEqual(i18n.tc(''), '') }) }) describe('not regist key', () => { it('should return key string', () => { assert.strictEqual(i18n.tc('foo.bar'), 'foo.bar') }) }) describe('sentence fragment', () => { it('should translate fragment', () => { assert.strictEqual(i18n.tc('hello world'), 'Hello World') }) it('should return replaced string if available', () => { assert.strictEqual( i18n.tc('Hello {0}', 1, ['kazupon']), 'Hello kazupon' ) }) it('should return key if unavailable', () => { assert.strictEqual(i18n.tc('Hello'), 'Hello') }) }) }) describe('format arguments', () => { describe('named', () => { it('should return replaced string', () => { assert.strictEqual( i18n.tc('plurals.format.named', 1, { name: 'kazupon' }), 'Hello kazupon, how are you?' ) }) }) describe('list', () => { it('should return replaced string', () => { assert.strictEqual( i18n.tc('plurals.format.list', 1, ['kazupon']), 'Hello kazupon, how are you?' ) }) }) }) describe('locale argument', () => { it('should return empty string', () => { assert.strictEqual(i18n.tc('plurals.car', 1, 'ja'), 'ザ・ワールド') }) }) describe('format & locale arguments', () => { it('should return replaced string', () => { assert.strictEqual( i18n.tc('plurals.format.list', 1, 'ja', ['kazupon']), 'こんにちは kazupon, ごきげんいかが?' ) }) }) describe('fallback', () => { it('should return fallback string', () => { assert.strictEqual( i18n.tc('plurals.fallback', 1, 'ja'), 'これはフォールバック' ) }) }) }) describe('i18n#te', () => { describe('existing key', () => { it('should return true', () => { assert(i18n.te('message.hello') === true) }) it('should return true with locale', () => { assert(i18n.te('message.hello', 'ja') === true) }) }) describe('not existing key', () => { it('should return false', () => { assert(i18n.te('message.hallo') === false) }) it('should return false with locale', () => { assert(i18n.te('message.hello', 'xx') === false) }) }) }) describe('$t', () => { describe('en locale', () => { it('should translate an english', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$t('message.hello'), messages.en.message.hello) }) }) describe('ja locale', () => { it('should translate a japanese', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$t('message.hello', 'ja'), messages.ja.message.hello) }) }) describe('key argument', () => { describe('not specify', () => { it('should return empty string', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$t(), '') }) }) describe('empty string', () => { it('should return empty string', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$t(''), '') }) }) describe('not regist key', () => { it('should return key string', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$t('foo.bar'), 'foo.bar') }) }) describe('sentence fragment', () => { it('should translate fragment', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$t('hello world'), 'Hello World') }) it('should return replaced string if available', () => { const vm = new Vue({ i18n }) assert.strictEqual( vm.$t('Hello {0}', ['kazupon']), 'Hello kazupon' ) }) it('should return key if unavailable', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$t('Hello'), 'Hello') }) }) }) describe('format arguments', () => { describe('named', () => { it('should return replaced string', () => { const vm = new Vue({ i18n }) assert.strictEqual( vm.$t('message.format.named', { name: 'kazupon' }), 'Hello kazupon, how are you?' ) }) }) describe('list', () => { it('should return replaced string', () => { const vm = new Vue({ i18n }) assert.strictEqual( vm.$t('message.format.list', ['kazupon']), 'Hello kazupon, how are you?' ) }) }) }) describe('locale argument', () => { it('should return empty string', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$t('message.hello', 'ja'), messages.ja.message.hello) }) }) describe('format & locale arguments', () => { it('should return replaced string', () => { const vm = new Vue({ i18n }) assert.strictEqual( vm.$t('message.format.list', 'ja', ['kazupon']), 'こんにちは kazupon, ごきげんいかが?' ) }) }) describe('fallback', () => { it('should return fallback string', () => { const vm = new Vue({ i18n }) assert.strictEqual( vm.$t('message.fallback', 'ja'), messages.en.message.fallback ) }) }) }) describe('$tc', () => { describe('en locale', () => { it('should translate plural english', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$tc('plurals.car', 1), 'car') }) }) describe('multi plural check', () => { it('should fetch pluralized string', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$tc('plurals.car', 2), 'cars') }) }) describe('key argument', () => { describe('not specify', () => { it('should return empty string', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$tc(), '') }) }) describe('empty string', () => { it('should return empty string', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$tc(''), '') }) }) describe('not regist key', () => { it('should return key string', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$tc('foo.bar'), 'foo.bar') }) }) describe('sentence fragment', () => { it('should translate fragment', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$tc('hello world'), 'Hello World') }) it('should return replaced string if available', () => { const vm = new Vue({ i18n }) assert.strictEqual( vm.$tc('Hello {0}', 1, ['kazupon']), 'Hello kazupon' ) }) it('should return key if unavailable', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$tc('Hello'), 'Hello') }) }) }) describe('format arguments', () => { describe('named', () => { it('should return replaced string', () => { const vm = new Vue({ i18n }) assert.strictEqual( vm.$tc('plurals.format.named', 1, { name: 'kazupon' }), 'Hello kazupon, how are you?' ) }) }) describe('list', () => { it('should return replaced string', () => { const vm = new Vue({ i18n }) assert.strictEqual( vm.$tc('plurals.format.list', 1, ['kazupon']), 'Hello kazupon, how are you?' ) }) }) }) describe('locale argument', () => { it('should return empty string', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$tc('plurals.car', 1, 'ja'), 'ザ・ワールド') }) }) describe('format & locale arguments', () => { it('should return replaced string', () => { const vm = new Vue({ i18n }) assert.strictEqual( vm.$tc('plurals.format.list', 1, 'ja', ['kazupon']), 'こんにちは kazupon, ごきげんいかが?' ) }) }) describe('fallback', () => { it('should return fallback string', () => { const vm = new Vue({ i18n }) assert.strictEqual( vm.$tc('plurals.fallback', 2, 'ja'), 'ザ・ワールド' ) }) }) }) describe('$te', () => { describe('existing key', () => { it('should return true', () => { const vm = new Vue({ i18n }) assert(vm.$te('message.hello') === true) }) it('should return true with locale', () => { const vm = new Vue({ i18n }) assert(vm.$te('message.hello', 'ja') === true) }) }) describe('not existing key', () => { it('should return false', () => { const vm = new Vue({ i18n }) assert(vm.$te('message.hallo') === false) }) it('should return false with locale', () => { const vm = new Vue({ i18n }) assert(vm.$te('message.hello', 'xx') === false) }) }) }) describe('i18n#locale', () => { let el beforeEach(() => { el = document.createElement('div') document.body.appendChild(el) }) it('should be reactivity translate', done => { const vm = new Vue({ el, i18n, render (h) { return h('p', {}, [this.$t('message.hello')]) } }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.textContent, messages.en.message.hello) i18n.locale = 'ja' // set japanese }).then(() => { assert.strictEqual(vm.$el.textContent, messages.ja.message.hello) }).then(done) }) }) describe('i18n#fallbackLocale', () => { let el beforeEach(() => { el = document.createElement('div') document.body.appendChild(el) }) it('should be reactivity translate', done => { const vm = new Vue({ el, i18n, render (h) { return h('p', {}, [this.$t('message.fallback1')]) } }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.textContent, 'message.fallback1') i18n.fallbackLocale = 'ja' // set fallback locale }).then(() => { assert.strictEqual(vm.$el.textContent, messages.ja.message.fallback1) }).then(done) }) }) describe('i18n#availableLocales', () => { it('should return locales defined in messages in lexical order', () => { assert.deepStrictEqual(i18n.availableLocales, ['en', 'ja']) }) }) let desc = VueI18n.availabilities.dateTimeFormat ? describe : describe.skip desc('i18n#d', () => { let dt beforeEach(() => { i18n = new VueI18n({ locale: 'en-US', fallbackLocale: 'ja-JP', dateTimeFormats }) dt = new Date(Date.UTC(2012, 11, 20, 3, 0, 0)) }) describe('arguments nothing', () => { it('should be formatted', () => { assert.strictEqual(i18n.d(dt), '12/20/2012') }) }) describe('number value', () => { it('should be formatted', () => { assert.strictEqual(i18n.d(dt.getTime()), '12/20/2012') }) }) describe('key argument', () => { // NOTE: avoid webkit(phatomjs/safari) & Intl polyfill wired localization... isChrome && it('should be formatted', () => { assert.strictEqual(i18n.d(dt, 'short'), '12/19/2012, 10:00 PM') }) }) describe('locale argument', () => { describe('with second argument', () => { // NOTE: avoid webkit(phatomjs/safari) & Intl polyfill wired localization... isChrome && it('should be formatted', () => { assert.strictEqual(i18n.d(dt, 'short', 'ja-JP'), '2012/12/20 12:00') }) }) describe('with object argument', () => { // NOTE: avoid webkit(phatomjs/safari) & Intl polyfill wired localization... isChrome && it('should be formatted', () => { assert.strictEqual(i18n.d(dt, { key: 'short', locale: 'ja-JP' }), '2012/12/20 12:00') }) }) }) describe('fallback', () => { // NOTE: avoid webkit(phatomjs/safari) & Intl polyfill wired localization... isChrome && it('should be formatted', () => { assert.strictEqual(i18n.d(dt, 'long'), '2012/12/20 12:00:00') }) }) }) desc = VueI18n.availabilities.numberFormat ? describe : describe.skip desc('i18n#n', () => { let money beforeEach(() => { i18n = new VueI18n({ locale: 'en-US', fallbackLocale: 'ja-JP', numberFormats }) money = 10100 }) describe('arguments nothing', () => { it('should be formatted', () => { assert.strictEqual(i18n.n(money), '10,100') }) }) describe('key argument', () => { it('should be formatted', () => { assert.strictEqual(i18n.n(money, 'currency'), '$10,100.00') }) }) describe('locale argument', () => { describe('with second argument', () => { it('should be formatted', () => { assert.strictEqual(i18n.n(money, 'currency', 'ja-JP'), '¥10,100') }) }) describe('with object argument', () => { it('should be formatted', () => { assert.strictEqual(i18n.n(money, { key: 'currency', locale: 'ja-JP' }), '¥10,100') }) }) }) describe('explicit options argument', () => { describe('without key', () => { it('should be formatted', () => { assert.strictEqual(i18n.n(money, { style: 'currency', currency: 'JPY' }), '¥10,100') }) it('should respect other number options', () => { const options = { style: 'currency', currency: 'EUR', currencyDisplay: 'symbol' } assert.strictEqual(i18n.n(money, options), '€10,100.00') }) }) describe('with key', () => { it('should be formatted', () => { assert.strictEqual(i18n.n(money, { key: 'currency', currency: 'JPY' }), '¥10,100') }) it('should respect other number options', () => { const options = { key: 'currency', currency: 'EUR', currencyDisplay: 'symbol' } assert.strictEqual(i18n.n(money, options), '€10,100.00') }) }) }) describe('fallback', () => { it('should be formatted', () => { assert.strictEqual(i18n.n(0.9, 'percent'), '90%') }) }) }) }) ================================================ FILE: test/unit/component.test.js ================================================ import dateTimeFormats from './fixture/datetime' import numberFormats from './fixture/number' describe('component translation', () => { let vm, i18n const dt = new Date(Date.UTC(2012, 11, 20, 3, 0, 0)) const money = 101 const messages = { 'en-US': { who: 'root', fallback: 'fallback' }, 'ja-JP': { who: 'ルート', fallback: 'フォールバック' } } beforeEach(done => { i18n = new VueI18n({ locale: 'ja-JP', messages, dateTimeFormats, numberFormats }) const el = document.createElement('div') vm = new Vue({ i18n, components: { child1: { // translation with component i18n: { locale: 'en-US', sync: false, messages: { 'en-US': { who: 'child1' }, 'ja-JP': { who: '子1' } }, sharedMessages: { // shared messages for child1 component 'en-US': { foo: { bar: 'bar' } }, 'ja-JP': { foo: { bar: 'バー' } } } }, components: { 'sub-child1': { // translation with root render (h) { return h('div', {}, [ h('p', { ref: 'who' }, [this.$t('who')]) ]) } } }, render (h) { return h('div', {}, [ h('p', { ref: 'who' }, [this.$t('who')]), h('p', { ref: 'fallback' }, [this.$t('fallback')]), h('p', { ref: 'datetime' }, [this.$d(dt, 'short')]), h('p', { ref: 'number' }, [this.$n(money, 'currency')]), h('p', { ref: 'shared' }, [this.$t('foo.bar')]), h('sub-child1', { ref: 'sub-child1' }) ]) } }, child2: { components: { 'sub-child2': { i18n: { messages: { 'en-US': { who: 'sub-child2' }, 'ja-JP': { who: 'サブの子2' } } }, render (h) { return h('div', {}, [ h('p', { ref: 'who' }, [this.$t('who')]) ]) } } }, render (h) { return h('div', {}, [ h('p', { ref: 'who' }, [this.$t('who')]), h('sub-child2', { ref: 'sub-child2' }) ]) } } }, render (h) { return h('div', {}, [ h('p', { ref: 'who' }, [this.$t('who')]), h('child1', { ref: 'child1' }), h('child2', { ref: 'child2' }) ]) } }).$mount(el) vm.$nextTick(done) }) it('should be translated', done => { const root = vm.$refs.who const child1 = vm.$refs.child1.$refs.who const child1Fallback = vm.$refs.child1.$refs.fallback const child1DateTime = vm.$refs.child1.$refs.datetime const child1Number = vm.$refs.child1.$refs.number const child1Shared = vm.$refs.child1.$refs.shared const child2 = vm.$refs.child2.$refs.who const subChild1 = vm.$refs.child1.$refs['sub-child1'].$refs.who const subChild2 = vm.$refs.child2.$refs['sub-child2'].$refs.who assert.strictEqual(root.textContent, 'ルート') assert.strictEqual(child1.textContent, 'child1') assert.strictEqual(child1Fallback.textContent, 'フォールバック') // NOTE: avoid webkit(phatomjs/safari) & Intl polyfill wired localization... isChrome && assert.strictEqual(child1DateTime.textContent, '12/19/2012, 10:00 PM') isChrome && assert.strictEqual(child1Number.textContent, '$101.00') assert.strictEqual(child1Shared.textContent, 'bar') assert.strictEqual(child2.textContent, 'ルート') assert.strictEqual(subChild1.textContent, 'ルート') assert.strictEqual(subChild2.textContent, 'サブの子2') // change locale i18n.locale = 'en-US' vm.$refs.child1.$i18n.locale = 'ja-JP' Vue.nextTick().then(() => { assert.strictEqual(root.textContent, 'root') assert.strictEqual(child1.textContent, '子1') assert.strictEqual(child1Fallback.textContent, 'fallback') // NOTE: avoid webkit(phatomjs/safari) & Intl polyfill wired localization... isChrome && assert.strictEqual(child1DateTime.textContent, '2012/12/20 12:00') isChrome && assert.strictEqual(child1Number.textContent, '¥101') assert.strictEqual(child2.textContent, 'root') assert.strictEqual(subChild1.textContent, 'root') assert.strictEqual(subChild2.textContent, 'sub-child2') vm.$destroy() }).then(done) }) it('fallbackRootWithEmptyString default to be true', done => { const el = document.createElement('div') let vm = new Vue({ i18n, components: { child: { // translation with component i18n: { locale: 'en-US', sync: false, messages: { 'en-US': { who: 'child' }, 'ja-JP': { who: '子', } }, }, components: { 'sub-child': { // translation with root i18n: { locale: 'ja-JP', sync: false, messages: { 'en-US': { who: 'sub-child' }, 'ja-JP': { who: '' } }, sharedMessages: { // shared messages for child1 component 'en-US': { foo: { bar: 'bar' } }, 'ja-JP': { foo: { bar: 'バー' } } } }, render (h) { return h('div', {}, [ h('p', { ref: 'who' }, [this.$t('who')]) ]) } } }, render (h) { return h('div', {}, [ h('p', { ref: 'who' }, [this.$t('who')]), h('sub-child', { ref: 'sub-child' }) ]) } }, }, render (h) { return h('div', {}, [ h('p', { ref: 'who' }, [this.$t('who')]), h('child', { ref: 'child' }), ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.child.$refs['sub-child'].$refs.who.textContent, 'ルート') }).then(done) }) it('fallbackRootWithEmptyString should work when set to false', done => { const el = document.createElement('div') let vm = new Vue({ i18n, components: { child: { // translation with component i18n: { locale: 'en-US', sync: false, messages: { 'en-US': { who: 'child' }, 'ja-JP': { who: '子', } }, }, components: { 'sub-child': { // translation with root i18n: { locale: 'ja-JP', sync: false, fallbackRootWithEmptyString: false, messages: { 'en-US': { who: 'sub-child' }, 'ja-JP': { who: '' } }, sharedMessages: { // shared messages for child1 component 'en-US': { foo: { bar: 'bar' } }, 'ja-JP': { foo: { bar: 'バー' } } } }, render (h) { return h('div', {}, [ h('p', { ref: 'who' }, [this.$t('who')]) ]) } } }, render (h) { return h('div', {}, [ h('p', { ref: 'who' }, [this.$t('who')]), h('sub-child', { ref: 'sub-child' }) ]) } }, }, render (h) { return h('div', {}, [ h('p', { ref: 'who' }, [this.$t('who')]), h('child', { ref: 'child' }), ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.child.$refs['sub-child'].$refs.who.textContent, '') }).then(done) }) }) ================================================ FILE: test/unit/custom_blocks.test.js ================================================ describe('custom blocks', () => { let i18n beforeEach(() => { i18n = new VueI18n({ locale: 'ja', messages: { en: { who: 'root' }, ja: { who: 'ルート' } } }) }) describe('json string', () => { it('should be translated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, components: { child: { __i18n: [JSON.stringify({ en: { who: 'child' }, ja: { who: '子' } })], render (h) { return h('div', {}, [ h('p', { ref: 'who' }, [this.$t('who')]) ]) } } }, render (h) { return h('div', {}, [h('child', { ref: 'child' })]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.child.$refs.who.textContent, '子') i18n.locale = 'en' }).then(() => { assert.strictEqual(vm.$refs.child.$refs.who.textContent, 'child') }).then(done) }) }) describe('invalid json string', () => { it('should be fallbacked translation', done => { const spy = sinon.spy(console, 'warn') const el = document.createElement('div') const vm = new Vue({ i18n, components: { child: { __i18n: 'foo', render (h) { return h('div', {}, [ h('p', { ref: 'who' }, [this.$t('who')]) ]) } } }, render (h) { return h('div', {}, [h('child', { ref: 'child' })]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.child.$refs.who.textContent, 'ルート') i18n.locale = 'en' }).then(() => { assert.strictEqual(vm.$refs.child.$refs.who.textContent, 'root') spy.restore() }).then(done) }) }) describe('sharedMessages option', () => { it('should be translated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, components: { child: { __i18n: [JSON.stringify({ en: { who: 'child' }, ja: { who: '子' } })], i18n: { sharedMessages: { en: { foo: 'foo' }, ja: { foo: 'フー' } } }, render (h) { return h('div', {}, [ h('p', { ref: 'foo' }, [this.$t('foo')]) ]) } } }, render (h) { return h('div', {}, [h('child', { ref: 'child' })]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.child.$refs.foo.textContent, 'フー') i18n.locale = 'en' }).then(() => { assert.strictEqual(vm.$refs.child.$refs.foo.textContent, 'foo') }).then(done) }) }) describe('bridge mode', () => { it('should be translated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, components: { child: { __i18nBridge: [JSON.stringify({ en: { who: 'child' }, ja: { who: '子' } })], render (h) { return h('div', {}, [ h('p', { ref: 'who' }, [this.$t('who')]) ]) } } }, render (h) { return h('div', {}, [h('child', { ref: 'child' })]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.child.$refs.who.textContent, '子') i18n.locale = 'en' }).then(() => { assert.strictEqual(vm.$refs.child.$refs.who.textContent, 'child') }).then(done) }) }) }) ================================================ FILE: test/unit/datetime.test.js ================================================ import dateTimeFormats from './fixture/datetime' const desc = VueI18n.availabilities.dateTimeFormat ? describe : describe.skip desc('datetime format', () => { describe('dateTimeFormats', () => { it('should be worked', done => { const i18n = new VueI18n({ locale: 'en-US', dateTimeFormats }) Vue.nextTick().then(() => { assert.deepEqual(dateTimeFormats, i18n.dateTimeFormats) }).then(done) }) }) describe('getDateTimeFormat / setDateTimeFormat', () => { it('should be worked', done => { const i18n = new VueI18n({ locale: 'en-US', dateTimeFormats }) const el = document.createElement('div') document.body.appendChild(el) const dt = new Date(Date.UTC(2012, 11, 20, 3, 0, 0)) const vm = new Vue({ i18n, render (h) { return h('p', { ref: 'text' }, [this.$d(dt, 'short')]) } }).$mount(el) const { text } = vm.$refs const zhFormat = { short: { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' } } Vue.nextTick().then(() => { // NOTE: avoid webkit(phatomjs/safari) & Intl polyfill wired localization... isChrome && assert.strictEqual(text.textContent, '12/19/2012, 10:00 PM') i18n.setDateTimeFormat('zh-CN', zhFormat) assert.deepEqual(i18n.getDateTimeFormat('zh-CN'), zhFormat) i18n.locale = 'zh-CN' }).then(() => { // NOTE: avoid webkit(phatomjs/safari) & Intl polyfill wired localization... isChrome && assert.strictEqual(text.textContent, '2012/12/20 12:00') }).then(done) }) }) describe('mergeDateTimeFormat', () => { it('should be merged', () => { const i18n = new VueI18n({ locale: 'ja-JP', dateTimeFormats }) const foo = { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' } i18n.mergeDateTimeFormat('en-US', { foo }) assert.deepEqual(foo, i18n.getDateTimeFormat('en-US').foo) }) }) describe('fallback', () => { it('should be fallbacked', done => { const i18n = new VueI18n({ locale: 'en-uk', fallbackLocale: ['de', 'en-us'], dateTimeFormats: { de: { short: { day: '2-digit', month: '2-digit', year: '2-digit' } } } }) const el = document.createElement('div') document.body.appendChild(el) const dt = new Date(Date.UTC(2012, 11, 20, 3, 0, 0)) const vm = new Vue({ i18n, render (h) { return h('p', { ref: 'text' }, [this.$d(dt, 'short')]) } }).$mount(el) const { text } = vm.$refs Vue.nextTick().then(() => { assert.strictEqual(text.textContent, '20.12.12') }).then(done) }) }) }) ================================================ FILE: test/unit/directive.test.js ================================================ import messages from './fixture/index' describe('custom directive', () => { let i18n beforeEach(() => { i18n = new VueI18n({ locale: 'en', messages }) }) function createVM (options) { const el = document.createElement('div') return new Vue(options).$mount(el) } describe('v-t', () => { describe('string literal', () => { it('should be translated', done => { const vm = createVM({ i18n, render (h) { //

return h('p', { ref: 'text', directives: [{ name: 't', rawName: 'v-t', value: ('message.hello'), expression: "'message.hello'" }] }) } }) Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.text.textContent, messages.en.message.hello) assert.strictEqual(vm.$refs.text._vt, messages.en.message.hello) vm.$forceUpdate() }).then(() => { assert.strictEqual(vm.$refs.text.textContent, messages.en.message.hello) assert.strictEqual(vm.$refs.text._vt, messages.en.message.hello) }).then(done) }) }) describe('object', () => { it('should be translated', done => { const vm = createVM({ i18n, data: { msgPath: 'message.format.named' }, render (h) { //

return h('p', { ref: 'text', directives: [{ name: 't', rawName: 'v-t', value: ({ path: this.msgPath, locale: 'ja', args: { name: 'kazupon' } }), expression: "{ path: msgPath, locale: 'ja', args: { name: 'kazupon' } }" }] }) } }) const expected = 'こんにちは kazupon, ごきげんいかが?' Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.text.textContent, expected) assert.strictEqual(vm.$refs.text._vt, expected) vm.$forceUpdate() }).then(() => { assert.strictEqual(vm.$refs.text.textContent, expected) assert.strictEqual(vm.$refs.text._vt, expected) }).then(done) }) }) describe('locale reactivity', () => { it('should be translated', done => { let expected = '' const vm = createVM({ i18n, data: { msgPath: 'message.format.named' }, render (h) { //

return h('p', { ref: 'text', directives: [{ name: 't', rawName: 'v-t', value: ({ path: this.msgPath, args: { name: 'kazupon' } }), expression: "{ path: msgPath, args: { name: 'kazupon' } }" }] }) } }) Vue.nextTick().then(() => { expected = 'Hello kazupon, how are you?' assert.strictEqual(vm.$refs.text.textContent, expected) assert.strictEqual(vm.$refs.text._vt, expected) assert.strictEqual(vm.$refs.text._locale, 'en') vm.$i18n.locale = 'ja' // change locale vm.$forceUpdate() }).then(() => { expected = 'こんにちは kazupon, ごきげんいかが?' assert.strictEqual(vm.$refs.text.textContent, expected) assert.strictEqual(vm.$refs.text._vt, expected) assert.strictEqual(vm.$refs.text._locale, 'ja') }).then(done) }) }) describe('not support warning', () => { it('should be warned', done => { const spy = sinon.spy(console, 'warn') createVM({ i18n, render (h) { //

return h('p', { ref: 'text', directives: [{ name: 't', rawName: 'v-t', value: ([1]), expression: '[1]' }] }) } }) Vue.nextTick().then(() => { assert(spy.notCalled === false) assert(spy.callCount === 1) spy.restore() }).then(done) }) }) describe('path required warning', () => { it('should be warned', done => { const spy = sinon.spy(console, 'warn') createVM({ i18n, render (h) { //

return h('p', { ref: 'text', directives: [{ name: 't', rawName: 'v-t', value: ({ locale: 'ja', args: { name: 'kazupon' } }), expression: "{ locale: 'ja', args: { name: 'kazupon' } }" }] }) } }) Vue.nextTick().then(() => { assert(spy.notCalled === false) assert(spy.callCount === 1) spy.restore() }).then(done) }) }) describe('VueI18n instance warning', () => { it('should be warned', done => { const spy = sinon.spy(console, 'warn') createVM({ render (h) { return h('p', { ref: 'text', directives: [{ name: 't', rawName: 'v-t', value: ('message.hello'), expression: "'message.hello'" }] }) } }) Vue.nextTick().then(() => { assert(spy.notCalled === false) assert(spy.callCount === 1) spy.restore() }).then(done) }) }) describe('pluralize', () => { it('should be singular', done => { const vm = createVM({ i18n, render (h) { //

return h('p', { ref: 'text', directives: [{ name: 't', rawName: 'v-t', value: ({ path: 'plurals.car', choice: 1 }), expression: { path: 'plurals.car', choice: 1 } }] }) } }) Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.text.textContent, 'car') assert.strictEqual(vm.$refs.text._vt, 'car') vm.$forceUpdate() }).then(() => { assert.strictEqual(vm.$refs.text.textContent, 'car') assert.strictEqual(vm.$refs.text._vt, 'car') }).then(done) }) it('should be plural', done => { const vm = createVM({ i18n, render (h) { //

return h('p', { ref: 'text', directives: [{ name: 't', rawName: 'v-t', value: ({ path: 'plurals.car', choice: 2 }), expression: { path: 'plurals.car', choice: 2 } }] }) } }) Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.text.textContent, 'cars') assert.strictEqual(vm.$refs.text._vt, 'cars') vm.$forceUpdate() }).then(() => { assert.strictEqual(vm.$refs.text.textContent, 'cars') assert.strictEqual(vm.$refs.text._vt, 'cars') }).then(done) }) it('should allow a zero choice', done => { const vm = createVM({ i18n, render (h) { //

return h('p', { ref: 'text', directives: [{ name: 't', rawName: 'v-t', value: ({ path: 'plurals.apple', choice: 0 }), expression: { path: 'plurals.apple', choice: 0 } }] }) } }) Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.text.textContent, 'no apples') assert.strictEqual(vm.$refs.text._vt, 'no apples') vm.$forceUpdate() }).then(() => { assert.strictEqual(vm.$refs.text.textContent, 'no apples') assert.strictEqual(vm.$refs.text._vt, 'no apples') }).then(done) }) }) describe('preserve content', () => { it('should clear element content on destroy by default', done => { const vm = createVM({ i18n, data: () => ({ visible: true }), render (h) { //

const directives = this.visible ? [{ name: 't', rawName: 'v-t', value: ('message.hello'), expression: "'message.hello'" }] : [] return h('p', { ref: 'text', directives }) } }) Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.text.textContent, messages.en.message.hello) vm.visible = false vm.$forceUpdate() }).then(() => { assert.strictEqual(vm.$refs.text.textContent, '') }).then(done) }) it('should not clear element content with "preserve" modifier', done => { const vm = createVM({ i18n, data: () => ({ visible: true }), render (h) { //

const directives = this.visible ? [{ name: 't', rawName: 'v-t', value: ('message.hello'), expression: "'message.hello'", modifiers: { preserve: true } }] : [] return h('p', { ref: 'text', directives }) } }) Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.text.textContent, messages.en.message.hello) vm.visible = false vm.$forceUpdate() }).then(() => { assert.strictEqual(vm.$refs.text.textContent, messages.en.message.hello) }).then(done) }) it('should not clear element content when "preserveDirectiveContent" i18nOption is set to true', done => { const vm = createVM({ i18n: new VueI18n({ locale: 'en', messages, preserveDirectiveContent: true }), data: () => ({ visible: true }), render (h) { //

const directives = this.visible ? [{ name: 't', rawName: 'v-t', value: ('message.hello'), expression: "'message.hello'" }] : [] return h('p', { ref: 'text', directives }) } }) Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.text.textContent, messages.en.message.hello) vm.visible = false vm.$forceUpdate() }).then(() => { assert.strictEqual(vm.$refs.text.textContent, messages.en.message.hello) }).then(done) }) }) }) }) ================================================ FILE: test/unit/escape_parameter_html.test.js ================================================ const messages = { en: { listformat: '{0}', nameformat: '{key}', } } describe('escapeParameterHtml', () => { it('Replacement parameters are escaped when escapeParameterHtml: true.', () => { const i18n = new VueI18n({ locale: 'en', messages, escapeParameterHtml: true }) // We should not escape the ampersand (&). assert(i18n.t('nameformat', { key: '<&"\'>' }) === '<&"'>') assert(i18n.t('listformat', ['<&"\'>']) === '<&"'>') assert(i18n.tc('nameformat', 1, { key: '<&"\'>' }).toString() === '<&"'>') assert(i18n.tc('listformat', 1, ['<&"\'>']).toString() === '<&"'>') }) it('Replacement parameters are not escaped when escapeParameterHtml: undefined.', () => { const i18n = new VueI18n({ locale: 'en', messages, }) assert(i18n.t('nameformat', { key: '<"\'>' }) === '<"\'>') assert(i18n.t('listformat', ['<"\'>']) === '<"\'>') }) }) ================================================ FILE: test/unit/fallback.test.js ================================================ import messages from './fixture/index' describe('kazupon#138 mmokross#2 - Fallback Locale as array for cascading fallbacks ...', () => { const types = [ { description: '... none', fallbackLocale: false, tests: [ { description: 'English', locale: 'en', expected: ['en'] }, { description: 'English (Great Britain)', locale: 'en-GB', expected: ['en-GB', 'en'] }, { description: 'German', locale: 'de', expected: ['de'] }, { description: 'German (Switzerland)', locale: 'de-CH', expected: ['de-CH', 'de'] } ] }, { description: '... simple', fallbackLocale: 'en', tests: [ { description: 'English', locale: 'en', expected: ['en'] }, { description: 'English (Great Britain)', locale: 'en-GB', expected: ['en-GB', 'en'] }, { description: 'German', locale: 'de', expected: ['de', 'en'] }, { description: 'German (Switzerland)', locale: 'de-CH', expected: ['de-CH', 'de', 'en'] } ] }, { description: '... array', fallbackLocale: ['en', 'ja'], tests: [ { description: 'English', locale: 'en', expected: ['en', 'ja'] }, { description: 'English (Great Britain)', locale: 'en-GB', expected: ['en-GB', 'en', 'ja'] }, { description: 'German', locale: 'de', expected: ['de', 'en', 'ja'] }, { description: 'German (Switzerland)', locale: 'de-CH', expected: ['de-CH', 'de', 'en', 'ja'] }, { description: 'Japanese', locale: 'ja', expected: ['ja', 'en'] } ] }, { description: '... complex', fallbackLocale: { 'de-CH': ['fr', 'it'], 'zh-Hant': ['zh-Hans'], 'es-CL': ['es-AR'], 'es': ['en-GB'], 'pt': ['es-AR'], 'default': ['en', 'da'] }, tests: [ { description: 'German (Switzerland)', locale: 'de-CH', expected: ['de-CH', 'fr', 'it', 'en', 'da'] }, { description: 'German (Switzerland) EXACT', locale: 'de-CH!', expected: ['de-CH', 'fr', 'it', 'en', 'da'] }, { description: 'German', locale: 'de', expected: ['de', 'en', 'da'] }, { description: 'Traditional Chinese', locale: 'zh-Hant', expected: ['zh-Hant', 'zh-Hans', 'zh', 'en', 'da'] }, { description: 'Spanish (Spain)', locale: 'es-SP', expected: ['es-SP', 'es', 'en-GB', 'en', 'da'] }, { description: 'Spanish (Spain) EXACT', locale: 'es-SP!', expected: ['es-SP', 'en', 'da'] }, { description: 'French', locale: 'fr', expected: ['fr', 'en', 'da'] }, { description: 'Portuguese (Brazil)', locale: 'pt-BR', expected: ['pt-BR', 'pt', 'es-AR', 'es', 'en-GB', 'en', 'da'] }, { description: 'Spanish (Chile)', locale: 'es-CL', expected: ['es-CL', 'es-AR', 'es', 'en-GB', 'en', 'da'] } ] } ] types.forEach(function (type) { describe(type.description, () => { let i18n beforeEach(() => { i18n = new VueI18n({ locale: 'en', fallbackLocale: type.fallbackLocale, messages, modifiers: { custom: str => str.replace(/[aeiou]/g, 'x') } }) }) type.tests.forEach(function (test) { it(test.description + ': ' + test.locale + ' should fallback to ' + test.expected, () => { // console.log(test.locale + ': --> ' + i18n.getLocaleChain(test.locale)) assert.deepEqual(i18n._getLocaleChain(test.locale), test.expected) }) }) }) }) }) describe('issue #868', () => { it('should be fallbacked', () => { const i18n = new VueI18n({ locale: 'de-CH', fallbackLocale: { 'de-CH': ['fr', 'it'], 'zh-Hant': ['zh-Hans'], 'es-CL': ['es-AR'], 'es': ['en-GB'], 'pt': ['es-AR'], default: ['en-US', 1, {}, null, false, undefined, 'en'] }, messages }) assert.deepEqual(i18n.t('message.hello'), 'the world') }) }) ================================================ FILE: test/unit/fixture/datetime.js ================================================ const formats = { 'en-US': { short: { // DD/MM/YYYY, hh:mm (AM|PM) year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' } }, 'ja-JP': { long: { // YYYY/MM/DD hh:mm:ss year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }, short: { // YYYY/MM/DD hh:mm year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' } } } // NOTE: avoid webkit(phatomjs/safari) & Intl timeZone don't support... if (isChrome) { formats['en-US']['short']['timeZone'] = 'America/New_York' formats['ja-JP']['short']['timeZone'] = 'Asia/Tokyo' formats['ja-JP']['long']['timeZone'] = 'Asia/Tokyo' } else { formats['en-US']['short']['timeZone'] = 'UTC' formats['ja-JP']['short']['timeZone'] = 'UTC' formats['ja-JP']['long']['timeZone'] = 'UTC' } export default formats ================================================ FILE: test/unit/fixture/index.js ================================================ export default { en: { message: { hello: 'the world', helloName: 'Hello {name}', hoge: 'hoge', empty: '', format: { named: 'Hello {name}, how are you?', list: 'Hello {0}, how are you?' }, fallback: 'this is fallback', link: '@:message.hello', linkHelloName: '@:message.helloName', linkLinkHelloName: '@:message.linkHelloName', linkEnd: 'This is a linked translation to @:message.hello', linkWithin: 'Isn\'t @:message.hello we live in great?', linkMultiple: 'Hello @:message.hoge!, isn\'t @:message.hello great?', linkBrackets: 'Hello @:(message.hoge). Isn\'t @:(message.hello) great?', linkHyphen: '@:hyphen-hello', linkUnderscore: '@:underscore_hello', linkPipe: '@:pipe|hello', linkColon: '@:(colon:hello)', linkList: '@:message.hello: {0} {1}', linkCaseLower: 'Please provide @.lower:message.homeAddress', linkCaseUpper: '@.upper:message.homeAddress', linkCaseCapitalize: '@.capitalize:message.homeAddress', linkCaseUnknown: '@.unknown:message.homeAddress', linkCaseCustom: '@.custom:message.homeAddress', homeAddress: 'home Address', circular1: 'Foo @:message.circular2', circular2: 'Bar @:message.circular3', circular3: 'Buz @:message.circular1', linkTwice: '@:message.hello: @:message.hello', 'Hello {0}': 'Hello {0}', 'hyphen-locale': 'hello hyphen', '1234': 'Number-based keys are found', '1mixedKey': 'Mixed keys are not found.', 'sálvame': 'save me' }, '0123a': 'Starts with number and contains non-number', '01234': 'Numeric only', '日本語': 'Japanese', 'hello world': 'Hello World', 'Hello {0}': 'Hello {0}', 'continue-with-new-account': 'continue with new account', 'hyphen-hello': 'hyphen the wolrd', /* eslint-disable */ underscore_hello: 'underscore the wolrd', 'colon:hello': 'hello colon', 'pipe|hello': 'hello pipe', /* eslint-enable */ underscore: '{helloMsg} world', plurals: { car: 'car | cars', apple: 'no apples | one apple | {count} apples', format: { named: 'Hello {name}, how are you? | Hi {name}, you look fine', list: 'Hello {0}, how are you? | Hi {0}, you look fine' }, fallback: 'this is fallback | this is a plural fallback' }, errors: [ 'this is 0 error code message', { internal1: 'this is internal 1 error message' }, [ 'this is nested array error 1' ] ], issues: { arrayBugs: ['bug1', 'bug2'] }, 'foo.bar.buz': 'hello flat key!' }, ja: { message: { hello: 'ザ・ワールド', hoge: 'ほげ', empty: '', format: { named: 'こんにちは {name}, ごきげんいかが?', list: 'こんにちは {0}, ごきげんいかが?' }, fallback1: 'これはフォールバック', 'hyphen-locale': 'こんにちは、ハイフン', '1234': '数字ベースのキーは見つかりませんでした。', '1mixedKey': 'ミックスされたキーは見つかりませんでした。', 'sálvame': '私を助けて' }, '01234': '数字のみ', '12345a': '数字で始まり、数字以外を含む', '日本語': '日本語', plurals: { car: 'ザ・ワールド | これはフォールバック', implicitPluralCount: 'こっちには来ない | count:{count}, n:{n}', format: { named: 'こんにちは {name}, ごきげんいかが? | こんにちは {name}, ごきげんいかが?', list: 'こんにちは {0}, ごきげんいかが?| こんにちは {0}, ごきげんいかが?' }, fallback: 'これはフォールバック | ザ・ワールド' }, errors: [ 'これはエラーコード0のエラーメッセージです。', { internal1: 'これは内部エラーコード1のエラーメッセージです。' }, [ 'これはネストされた配列のエラー1です。' ] ], issues: { arrayBugs: ['バグ1', 'バグ2'] }, 'foo.bar.buz': 'こんにちは、フラットなキーさん!' } } ================================================ FILE: test/unit/fixture/number.js ================================================ export default { 'en-US': { currency: { style: 'currency', currency: 'USD', currencyDisplay: 'symbol' }, decimal: { style: 'decimal', useGrouping: false } }, 'ja-JP': { currency: { style: 'currency', currency: 'JPY', currencyDisplay: 'symbol' }, numeric: { style: 'decimal', useGrouping: false }, percent: { style: 'percent', useGrouping: false } } } ================================================ FILE: test/unit/format.test.js ================================================ import { parse, compile } from '../../src/format' describe('parse', () => { describe('list', () => { it('should be parsed', () => { const tokens = parse('name: {0}, email: {1}') assert(tokens.length === 4) assert.strictEqual(tokens[0].type, 'text') assert.strictEqual(tokens[0].value, 'name: ') assert.strictEqual(tokens[1].type, 'list') assert.strictEqual(tokens[1].value, '0') assert.strictEqual(tokens[2].type, 'text') assert.strictEqual(tokens[2].value, ', email: ') assert.strictEqual(tokens[3].type, 'list') assert.strictEqual(tokens[3].value, '1') }) }) describe('named', () => { it('should be parsed', () => { const tokens = parse('name: {name}, email: {email}') assert(tokens.length === 4) assert.strictEqual(tokens[0].type, 'text') assert.strictEqual(tokens[0].value, 'name: ') assert.strictEqual(tokens[1].type, 'named') assert.strictEqual(tokens[1].value, 'name') assert.strictEqual(tokens[2].type, 'text') assert.strictEqual(tokens[2].value, ', email: ') assert.strictEqual(tokens[3].type, 'named') assert.strictEqual(tokens[3].value, 'email') }) }) describe('rails i18n format syntax', () => { it('should be parsed', () => { const tokens = parse('name: %{name}, email: %{email}') assert(tokens.length === 4) assert.strictEqual(tokens[0].type, 'text') assert.strictEqual(tokens[0].value, 'name: ') assert.strictEqual(tokens[1].type, 'named') assert.strictEqual(tokens[1].value, 'name') assert.strictEqual(tokens[2].type, 'text') assert.strictEqual(tokens[2].value, ', email: ') assert.strictEqual(tokens[3].type, 'named') assert.strictEqual(tokens[3].value, 'email') }) }) describe('not support format', () => { it('should be parsed', () => { const tokens = parse('name: { name1}, email: {%email}') assert(tokens.length === 4) assert.strictEqual(tokens[0].type, 'text') assert.strictEqual(tokens[0].value, 'name: ') assert.strictEqual(tokens[1].type, 'unknown') assert.strictEqual(tokens[1].value, ' name1') assert.strictEqual(tokens[2].type, 'text') assert.strictEqual(tokens[2].value, ', email: ') assert.strictEqual(tokens[3].type, 'unknown') assert.strictEqual(tokens[3].value, '%email') }) }) }) describe('compile', () => { describe('list token', () => { it('should be compiled', () => { const tokens = parse('name: {0}, age: {1}') const compiled = compile(tokens, ['kazupon', '0x20']) assert(compiled.length === 4) assert.strictEqual(compiled[0], 'name: ') assert.strictEqual(compiled[1], 'kazupon') assert.strictEqual(compiled[2], ', age: ') assert.strictEqual(compiled[3], '0x20') }) }) describe('named token', () => { it('should be compiled', () => { const tokens = parse('name: {name}, age: {age}') const compiled = compile(tokens, { name: 'kazupon', age: '0x20' }) assert(compiled.length === 4) assert.strictEqual(compiled[0], 'name: ') assert.strictEqual(compiled[1], 'kazupon') assert.strictEqual(compiled[2], ', age: ') assert.strictEqual(compiled[3], '0x20') }) it('should be compiled as unknown if not closed', () => { const tokens = parse('name: {name') const compiled = compile(tokens, { name: 'kazupon' }) assert(compiled.length === 1) assert.strictEqual(compiled[0], 'name: ') }) }) describe('unknown token', () => { it('should be compiled', () => { const spy = sinon.spy(console, 'warn') const tokens = parse('name: { name1}, email: {%email}') const compiled = compile(tokens, ['kazupon', '0x20']) assert(compiled.length === 2) assert.strictEqual(compiled[0], 'name: ') assert.strictEqual(compiled[1], ', email: ') assert(spy.notCalled === false) assert(spy.callCount === 2) spy.restore() }) }) describe('values unknown mode', () => { it('should be compiled with empty', () => { const compiled = compile([], 1) assert.deepEqual(compiled, []) }) }) describe('list token with named value', () => { it('should be compiled', () => { const tokens = parse('name: {0}, age: {1}') // list tokens const compiled = compile(tokens, { '0': 'kazupon', '1': '0x20' }) // named values assert(compiled.length === 4) assert.strictEqual(compiled[0], 'name: ') assert.strictEqual(compiled[1], 'kazupon') assert.strictEqual(compiled[2], ', age: ') assert.strictEqual(compiled[3], '0x20') }) }) describe('unmatch values mode', () => { it('should be warned', () => { const spy = sinon.spy(console, 'warn') const tokens = parse('name: {name}, age: {age}') // named tokens compile(tokens, ['kazupon', '0x20']) // list values assert(spy.notCalled === false) assert(spy.callCount === 2) spy.restore() }) }) }) ================================================ FILE: test/unit/format_custom.test.js ================================================ import messages from './fixture/index' describe('custom formatter', () => { describe('via i18n instance API calling', () => { it('should allows for specifying a custom formatter', done => { class CustomFormatter { interpolate (message, values) { assert.deepEqual({ name: 'joe' }, values) done() } } const formatter = new CustomFormatter() const i18n = new VueI18n({ locale: 'en', messages, formatter }) i18n.t('message.hello', 'ja', { name: 'joe' }) }) it('should interpolate messages without values', done => { class CustomFormatter { interpolate (message, values) { assert(values === null) done() } } const formatter = new CustomFormatter() const i18n = new VueI18n({ locale: 'en', messages, formatter }) i18n.t('message.hello') }) }) describe('via vue instance calling', () => { it('should allows for specifying a custom formatter', done => { const formatter = { interpolate: (message, values) => { assert.deepEqual([1, 2, 3], values) done() } } const vm = new Vue({ i18n: new VueI18n({ locale: 'en', messages, formatter }) }) vm.$t('message.hello', [1, 2, 3]) }) }) describe('via vue instance calling (mounted)', () => { let el beforeEach(done => { el = document.createElement('div') done() }) it('should be inherited by components', done => { new Vue({ i18n: new VueI18n({ locale: 'en', formatter: { interpolate: (message, values) => { assert.deepEqual({ name: 'user' }, values) done() return ['pass'] } } }), components: { 'child-1': { render (h) { return h('div', {}, [ h('p', {}, [this.$t('message', { name: 'user' })]) ]) }, i18n: { messages: { en: { message: 'hello {name}' } } } } }, render (h) { return h('div', {}, [ h('child-1') ]) } }).$mount(el) }) }) describe('i18n format getter/settter', () => { it('should be worked', done => { const i18n = new VueI18n({ locale: 'en', messages }) assert(i18n.formatter.constructor.name === 'BaseFormatter') const formatter = { interpolate: (message, values) => { assert.deepEqual([1, 2, 3], values) done() } } i18n.formatter = formatter i18n.t('message.hello', [1, 2, 3]) }) }) }) ================================================ FILE: test/unit/index.html ================================================ vue-i18n tests ================================================ FILE: test/unit/index.js ================================================ // import all helpers const helpersContext = require.context('../helpers', true) helpersContext.keys().forEach(helpersContext) // require all test files const testsContext = require.context('./', true, /\.test/) testsContext.keys().forEach(testsContext) ================================================ FILE: test/unit/interpolation.test.js ================================================ import Component from '../../src/components/interpolation' const messages = { en: { text: 'one: {0}', primitive: 'one: {0}, two: {1}', component: 'element: {0}, component: {1}', mixed: 'text: {x}, component: {y}', named: 'header: {header}, footer: {footer}', link: '@:primitive', term: 'I accept xxx {0}.', tos: 'Term of service', fallback: 'fallback from {0}' }, ja: { text: '一: {0}', 'I am {0}': '一: {0}' } } const components = { comp: { props: { msg: { type: String, default: '' } }, render (h) { return h('p', [this.msg]) } }, fallback: { i18n: { locale: 'en' }, render (h) { return h('i18n', { props: { path: 'fallback' } }, [ h('p', ['child']) ]) } } } describe('component interpolation', () => { let i18n beforeEach(() => { i18n = new VueI18n({ locale: 'en', messages }) }) describe('tag', () => { it('defaults to span', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'text' } }, [this._v('1')]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.outerHTML, 'one: 1') }).then(done) }) it('of type object (Component) should be correctly applied', done => { const MockElement = { render(h) { return h('strong', Object.values(this.$slots)); }, } const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'text', tag: MockElement } }, [this._v('1')]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.outerHTML, 'one: 1') }).then(done).catch(done) }) it('of type object (Functional Component) should be correctly applied', done => { const MockElement = { functional: true, render(h, { data, children }) { return h('em', data, children); }, } const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'text', tag: MockElement } }, [this._v('1')]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.outerHTML, 'one: 1') }).then(done).catch(done) }) it('of type string should be correctly applied', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'text', tag: 'b' } }, [this._v('1')]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.outerHTML, 'one: 1') }).then(done) }) it('of value true defaults to span', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'text', tag: true } }, [this._v('1')]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.outerHTML, 'one: 1') }).then(done) }) it('of value false does not apply a root container', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'text', tag: false } }, [this._v('1')]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.nodeType, Node.TEXT_NODE) assert.strictEqual(vm.$el.data, 'one: 1') }).then(done) }) }) describe('children', () => { describe('text nodes', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'text' } }, [this._v('1')]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.textContent, 'one: 1') }).then(done) }) }) describe('primitive nodes', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'primitive' } }, [ h('p', ['1']), h('p', ['2']) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, 'one:

1

, two:

2

') }).then(done) }) }) describe('empty text node between components', () => { it('should NOT be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'primitive' } }, [ h('p', ['1']), this._v(''), h('p', ['2']) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, 'one:

1

, two:

2

') }).then(done) }) }) describe('components', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, components, render (h) { return h('i18n', { props: { path: 'component' } }, [ h('p', ['1']), h('comp', { props: { msg: 'foo' } }) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, 'element:

1

, component:

foo

') }).then(done) }) }) describe('places prop', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'text', places: [1] } }) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.textContent, 'one: 1') }).then(done) }) }) describe('place prop on all children', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, components, render (h) { return h('i18n', { props: { path: 'component' } }, [ h('p', { props: { place: 0 } }, ['1']), h('comp', { props: { place: 1, msg: 'foo' } }) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, 'element:

1

, component:

foo

') }).then(done) }) }) describe('place prop on some children', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, components, render (h) { return h('i18n', { props: { path: 'component' } }, [ h('p', { props: { place: 1 } }, ['1']), h('comp', { props: { msg: 'foo' } }) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, 'element:

1

, component:

foo

') }).then(done) }) }) describe('places and place mixed', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, components, render (h) { return h('i18n', { props: { path: 'mixed', places: { 'x': 'foo' } } }, [ h('comp', { props: { msg: 'bar' }, attrs: { place: 'y' } }) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, 'text: foo, component:

bar

') }).then(done) }) }) describe('places set, place not set on all children', () => { it('should be warned', done => { const spy = sinon.spy(console, 'warn') const el = document.createElement('div') const vm = new Vue({ i18n, components, render (h) { return h('i18n', { props: { path: 'mixed', places: { 'x': 'foo' } } }, [ h('comp', { props: { msg: 'bar' } }) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, 'text: foo, component: ') assert(spy.notCalled === false) assert(spy.callCount === 1) spy.restore() }).then(done) }) }) describe('fallback', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, components, render (h) { return h('fallback') } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, 'fallback from

child

') }).then(done) }) }) describe('nested components', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, components, render (h) { return h('i18n', { props: { path: 'component' } }, [ h('p', ['1']), h('div', {}, [ h('i18n', { class: 'nested', props: { tag: 'div', path: 'component' } }, [ h('p', ['2']), h('comp', { props: { msg: 'nested' } }) ]) ]) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual( vm.$el.innerHTML, 'element:

1

, component:
element:

2

, component:

nested

' ) }).then(done) }) }) }) describe('linked', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'link' } }, [ h('p', ['1']), h('p', ['2']) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, 'one:

1

, two:

2

') }).then(done) }) }) describe('locale', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'text', locale: 'ja' } }, [ this._v('1') ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.textContent, '一: 1') }).then(done) }) }) describe('included translation locale message', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'term' } }, [ h('a', { domProps: { href: '/term', textContent: this.$t('tos') } }) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual( vm.$el.innerHTML, 'I accept xxx Term of service.' ) }).then(done) }) }) describe('slot', () => { describe('with default slot', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'text' }, slot: '' }, [this._v('1')]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.textContent, 'one: 1') }).then(done) }) }) describe('with named slots ', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'named' } }, [ h('template', { slot: 'header' }, [h('p', 'header')]), h('template', { slot: 'footer' }, [h('p', 'footer')]) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual( vm.$el.innerHTML, 'header:

header

, footer:

footer

' ) }).then(done) }) }) describe('primitive nodes', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'primitive' } }, [ h('template', { slot: '0' }, ['1']), h('template', { slot: '1' }, ['2']) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, 'one: 1, two: 2') }).then(done) }) }) describe('linked', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'link' } }, [ h('template', { slot: '0' }, ['1']), h('template', { slot: '1' }, ['2']) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, 'one: 1, two: 2') }).then(done) }) }) describe('included translation locale message', () => { it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'term' } }, [ h('template', { slot: '0' }, [ h('a', { domProps: { href: '/term', textContent: this.$t('tos') } }) ]) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual( vm.$el.innerHTML, 'I accept xxx Term of service.' ) }).then(done) }) }) describe('formatFallbackMessages', () => { let i18n beforeEach(() => { i18n = new VueI18n({ locale: 'en', messages, formatFallbackMessages: true }) }) it('should be interpolated', done => { const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'I am {0}' } }, [ h('template', { slot: '0' }, [ h('a', { domProps: { href: '/term', textContent: this.$t('tos') } }) ]) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual( vm.$el.innerHTML, 'I am Term of service' ) }).then(done) }) it('use ja message', done => { i18n.locale = 'ja' const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'I am {0}' } }, [ h('template', { slot: '0' }, [ h('a', { domProps: { href: '/term', textContent: this.$t('tos') } }) ]) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual( vm.$el.innerHTML, '一: Term of service' ) }).then(done) }) it('fallbackRoot has higher priority than formatFallbackMessages', done => { i18n = new VueI18n({ locale: 'ja', messages, fallbackLocale: 'en', formatFallbackMessages: true, fallbackRoot: true }) const el = document.createElement('div') const vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'I am {0}' } }, [ h('template', { slot: '0' }, [ h('a', { domProps: { href: '/term', textContent: this.$t('tos') } }) ]) ]) } }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual( vm.$el.innerHTML, '一: Term of service' ) }).then(done) }) }) }) describe('warnning in render', () => { it('should be warned', () => { const spy = sinon.spy(console, 'warn') Component.render(() => {}, { children: [], parent: {} }) assert(spy.notCalled === false) assert(spy.callCount === 1) spy.restore() }) }) }) ================================================ FILE: test/unit/issues.test.js ================================================ import messages from './fixture/index' import { parse } from '../../src/format' import VueI18n from '../../src' const compiler = require('vue-template-compiler') const delay = time => new Promise(resolve => setTimeout(resolve, time)) describe('issues', () => { let vm, i18n beforeEach(() => { i18n = new VueI18n({ locale: 'en', messages }) vm = new Vue({ i18n }) }) describe('#24', () => { it('should be translated', () => { assert.strictEqual( vm.$t('continue-with-new-account'), messages[vm.$i18n.locale]['continue-with-new-account'] ) }) }) describe('#35', () => { it('should be translated', () => { assert.strictEqual( vm.$t('underscore', { helloMsg: 'hello' }), 'hello world' ) }) }) describe('#42, #43', () => { it('should not be occurred error', () => { assert.strictEqual( vm.$t('message[\'hello\']'), messages[vm.$i18n.locale]['message']['hello'] ) }) }) describe('#51', () => { it('should be translated', () => { assert.strictEqual( vm.$t('message.hyphen-locale'), 'hello hyphen' ) }) }) describe('#91, #51', () => { it('should be translated', () => { const arrayMessages = messages[vm.$i18n.locale].issues.arrayBugs for (let i = 0; i < arrayMessages.length; i++) { const item = vm.$t('issues.arrayBugs')[i] assert.strictEqual(item, arrayMessages[i]) } }) }) describe('#97', () => { it('should be translated', () => { assert.strictEqual( vm.$t('message.1234'), messages[vm.$i18n.locale]['message']['1234'] ) assert.strictEqual( vm.$t('message.1mixedKey'), messages[vm.$i18n.locale]['message']['1mixedKey'] ) }) }) describe('#169', () => { it('should be translated', done => { const Component = Vue.extend({ __i18n: [JSON.stringify({ en: { custom: 'custom block!' } })], render (h) { return h('p', { ref: 'custom' }, [this.$t('custom')]) } }) const vm = new Component({ i18n }).$mount() Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.custom.textContent, 'custom block!') }).then(done) }) }) describe('#170', () => { it('should be translated', () => { assert.strictEqual(vm.$i18n.t('message.linkHyphen'), messages.en['hyphen-hello']) assert.strictEqual(vm.$i18n.t('message.linkUnderscore'), messages.en.underscore_hello) }) }) describe('#171', () => { it('should be translated', done => { vm = new Vue({ i18n, render (h) { return h('i18n', { props: { path: 'message.linkList' } }, [ h('strong', [this.$t('underscore_hello')]), h('strong', [this.$t('message.link')]) ]) } }).$mount() Vue.nextTick().then(() => { assert.strictEqual( vm.$el.innerHTML, 'the world: underscore the wolrd the world' ) }).then(done) }) }) describe('#172', () => { it('should be translated', done => { vm = new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { 'company-name': 'billy-bob\'s fine steaks.' } } }), components: { comp: { __i18n: [JSON.stringify({ en: { title: '@:company-name - yeee hawwww!!!' } })], render (h) { return h('p', { ref: 'title' }, [this.$t('title')]) } } }, render (h) { return h('div', [h('comp', { ref: 'comp' })]) } }).$mount() Vue.nextTick().then(() => { assert.strictEqual( vm.$refs.comp.$refs.title.textContent, 'billy-bob\'s fine steaks. - yeee hawwww!!!' ) }).then(done) }) }) describe('#173', () => { it('should be translated', done => { const Component = Vue.extend({ __i18n: [JSON.stringify({ en: { custom: 'custom block!' } })], render (h) { return h('p', { ref: 'custom' }, [this.$t('custom')]) } }) const vm = new Component({ i18n: new VueI18n({ locale: 'en' }) }).$mount() Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.custom.textContent, 'custom block!') }).then(done) }) }) describe('#174', () => { it('should be fallback', done => { vm = new Vue({ i18n: new VueI18n({ locale: 'en', fallbackLocale: 'ja', messages: { en: {}, ja: { msg: 'メッセージ' } } }), components: { comp: { i18n: { messages: { en: {}, ja: { hello: 'こんにちは' } } }, render (h) { return h('div', [ h('p', { ref: 'el1' }, [this.$t('hello')]), h('p', { ref: 'el2' }, [this.$t('msg')]) ]) } } }, render (h) { return h('div', [h('comp', { ref: 'comp' })]) } }).$mount() const el1 = vm.$refs.comp.$refs.el1 const el2 = vm.$refs.comp.$refs.el2 Vue.nextTick().then(() => { assert.strictEqual(el1.textContent, 'こんにちは') assert.strictEqual(el2.textContent, 'メッセージ') }).then(done) }) }) describe('#176', () => { it('should be translated', done => { vm = new Vue({ i18n: new VueI18n({ locale: 'xx', fallbackLocale: 'en', messages: { en: { 'alpha': '[EN] alpha {gustav} value', 'bravo': '[EN] bravo {gustav} value', 'charlie': '[EN] charlie {0} value', 'delta': '[EN] delta {0} value' }, xx: { 'bravo': '[XX] bravo {gustav} value', 'delta': '[XX] delta {0} value' } } }), render (h) { return h('div', [ h('p', { ref: 'el1' }, [this.$t('alpha', { gustav: 'injected' })]), h('p', { ref: 'el2' }, [this.$t('bravo', { gustav: 'injected' })]), h('p', { ref: 'el3' }, [this.$t('charlie', ['injected'])]), h('p', { ref: 'el4' }, [this.$t('delta', ['injected'])]) ]) } }).$mount() Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.el1.textContent, '[EN] alpha injected value') assert.strictEqual(vm.$refs.el2.textContent, '[XX] bravo injected value') assert.strictEqual(vm.$refs.el3.textContent, '[EN] charlie injected value') assert.strictEqual(vm.$refs.el4.textContent, '[XX] delta injected value') }).then(done) }) }) describe('#191', () => { it('should be parsed', () => { const tokens = parse('{deposit}% PREPAYMENT') assert(tokens.length === 2) assert.strictEqual(tokens[0].type, 'named') assert.strictEqual(tokens[0].value, 'deposit') assert.strictEqual(tokens[1].type, 'text') assert.strictEqual(tokens[1].value, '% PREPAYMENT') }) }) describe('#200', () => { it('should be translated', () => { const el = document.createElement('div') const Constructor = Vue.extend({ i18n }) const vm = new Constructor({ render (h) { return h('p', { ref: 'text' }, [this.$t('message.hello')]) } }).$mount(el) assert.strictEqual(vm.$refs.text.textContent, messages.en.message.hello) }) }) describe('#203', () => { it('should be translated', done => { const App = { render (h) { return h('p', { ref: 'app' }, [this.$t('hello')]) } } vm = new Vue({ render (h) { return h({ components: { App }, render (h) { return h('app') }, i18n: new VueI18n({ locale: 'en', messages: { en: { 'hello': 'hello 203' }, ja: { 'hello': 'こんにちは 203' } } }) }) } }).$mount() Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, 'hello 203') }).then(done) }) }) describe('#247', () => { it('should be warned if circular reference in linked locale message', () => { const spy = sinon.spy(console, 'warn') assert.strictEqual(vm.$i18n.t('message.circular1'), 'Foo Bar Buz @:message.circular1') assert(spy.notCalled === false) assert(spy.callCount === 1) spy.restore() }) it('should not be warned if same non-circular link used repeatedly', () => { const spy = sinon.spy(console, 'warn') assert.strictEqual(vm.$i18n.t('message.linkTwice'), 'the world: the world') assert(spy.notCalled === true) assert(spy.callCount === 0) spy.restore() }) }) describe('#349', () => { it('should be existed', done => { assert(vm.$te('foo.bar.buz') === true) done() }) }) describe('#377', () => { it('should be destroyed', done => { const el = document.createElement('div') const template = `

TIMEOUT : {{ timeout }}

{{ $t('CANNOT_REPRODUCE_WITHOUT_THIS') }}
` const { render, staticRenderFns } = compiler.compileToFunctions(template) const vm = new Vue({ i18n: new VueI18n({ locale: 'id' }), data () { return { timeout: false } }, methods: { startLoading: function () { this.timeout = true setTimeout(() => { this.timeout = false }, 100) } }, render, staticRenderFns }).$mount(el) Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.el1.outerHTML, '
SHOULD_NOT_DISPLAY_WHEN_TIMEOUT_EQUAL_TRUE
') vm.startLoading() delay(50).then(() => { assert.strictEqual(vm.$refs.el2.outerHTML, '
CANNOT_REPRODUCE_WITHOUT_THIS
') delay(60).then(() => { assert.strictEqual(vm.$refs.el1.outerHTML, '
SHOULD_NOT_DISPLAY_WHEN_TIMEOUT_EQUAL_TRUE
') done() }) }) }) }) }) describe('#398', () => { it('should return true', () => { assert.strictEqual(vm.$te('0123a'), true) assert.strictEqual(vm.$te('01234'), true) assert.strictEqual(vm.$te('message.1234'), true) }) }) describe('#430', () => { it('should be translated', () => { assert.strictEqual( vm.$t('日本語'), messages[vm.$i18n.locale]['日本語'] ) assert.strictEqual( vm.$t('message.sálvame'), messages[vm.$i18n.locale]['message']['sálvame'] ) }) }) describe('#450', () => { it('shoulbe be translated with v-t', done => { const vm = new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { hello: 'hi there!' } } }), render (h) { //

return h('p', { ref: 'text', directives: [{ name: 't', rawName: 'v-t', value: ('hello'), expression: "'hello'" }] }) } }).$mount(document.createElement('div')) Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.text.textContent, 'hi there!') }).then(() => { vm.$i18n.setLocaleMessage('en', { hello: 'hello there!' }) vm.$forceUpdate() }).then(() => { assert.strictEqual(vm.$refs.text.textContent, 'hello there!') }).then(done) }) }) describe('#453', () => { it('should be handled root vm instance', done => { const vm = new Vue({ i18n: new VueI18n({ locale: 'en', missing: (locale, key, instance) => { assert.strictEqual('ja', locale) assert.strictEqual('foo.bar', key) assert(vm === instance) done() } }), components: { child: { i18n: { locale: 'ja' }, render (h) { return h('p', ['hello child']) } } }, render (h) { return h('div', [ h('child', { ref: 'child' }) ]) } }).$mount() vm.$nextTick(() => { vm.$refs.child.$i18n.t('foo.bar', 'ja') }) }) }) describe('#458', () => { it('should be merged locale message', done => { const vm = new Vue({ i18n: new VueI18n({ locale: 'en', messages: { hello: 'hello world!' } }), render (h) { return h('div', [ h('p', { ref: 'text1' }, [this.$t('key1')]), h('p', { ref: 'text2' }, [this.$t('shared.key1')]), h('p', { ref: 'text3' }, [this.$t('key2')]), h('p', { ref: 'text4' }, [this.$t('shared.key2')]) ]) } }).$mount() Vue.nextTick().then(() => { vm.$i18n.mergeLocaleMessage('en', { key1: 'Hello Module 1', shared: { key1: 'Hello Module 1 shared key 1' } }) vm.$i18n.mergeLocaleMessage('en', { key2: 'Hello Module 2', shared: { key2: 'Hello Module 2 shared key 2' } }) }).then(() => { assert.strictEqual(vm.$refs.text1.textContent, 'Hello Module 1') assert.strictEqual(vm.$refs.text2.textContent, 'Hello Module 1 shared key 1') assert.strictEqual(vm.$refs.text3.textContent, 'Hello Module 2') assert.strictEqual(vm.$refs.text4.textContent, 'Hello Module 2 shared key 2') }).then(done) }) }) describe('#78, #464', () => { it('should fallback to default pluralization', () => { // / Test default pluralization rule (english) const i18n = new VueI18n({ locale: 'en', messages: { 'en': { test: 'no tests | 1 test | {n} tests' } } }) assert.strictEqual(i18n.tc('test', 1), '1 test') assert.strictEqual(i18n.tc('test', 0), 'no tests') assert.strictEqual(i18n.tc('test', 10), '10 tests') }) it('should use custom pluralization if available', () => { // Test custom pluralization rule (slavic languages) function slavicPluralization (choice, choicesLength) { if (choice === 0) { return 0 } const teen = choice > 10 && choice < 20 const endsWithOne = choice % 10 === 1 if (choicesLength < 4) { return (!teen && endsWithOne) ? 1 : 2 } if (!teen && endsWithOne) { return 1 } if (!teen && choice % 10 >= 2 && choice % 10 <= 4) { return 2 } return (choicesLength < 4) ? 2 : 3 } let i18n = new VueI18n({ locale: 'en', messages: { 'en': { test: 'no tests | 1 test | {n} tests' }, 'ru': { test: 'нет тестов | 1 тест | {n} теста | {n} тестов' } }, pluralizationRules: { 'ru': slavicPluralization } }) assert.strictEqual(i18n.tc('test', 1), '1 test') assert.strictEqual(i18n.tc('test', 0), 'no tests') assert.strictEqual(i18n.tc('test', 10), '10 tests') i18n.locale = 'ru' assert.strictEqual(i18n.tc('test', 1), '1 тест') assert.strictEqual(i18n.tc('test', 3), '3 теста') assert.strictEqual(i18n.tc('test', 0), 'нет тестов') assert.strictEqual(i18n.tc('test', 10), '10 тестов') i18n = new VueI18n({ locale: 'ru', messages: { ru: { car: '0 машин | {n} машина | {n} машины | {n} машин' } }, pluralizationRules: { ru: slavicPluralization } }) vm = new Vue({ i18n }) assert.strictEqual(vm.$tc('car', 0), '0 машин') assert.strictEqual(vm.$tc('car', 1), '1 машина') assert.strictEqual(vm.$tc('car', 2), '2 машины') assert.strictEqual(vm.$tc('car', 4), '4 машины') assert.strictEqual(vm.$tc('car', 12), '12 машин') assert.strictEqual(vm.$tc('car', 21), '21 машина') }) it('ensures backward-compatibility with #451', () => { const defaultImpl = VueI18n.prototype.getChoiceIndex VueI18n.prototype.getChoiceIndex = function (choice, choicesLength) { if (this.locale !== 'ru') { return defaultImpl.apply(this, arguments) } if (choice === 0) { return 0 } const teen = choice > 10 && choice < 20 const endsWithOne = choice % 10 === 1 if (choicesLength < 4) { return (!teen && endsWithOne) ? 1 : 2 } if (!teen && endsWithOne) { return 1 } if (!teen && choice % 10 >= 2 && choice % 10 <= 4) { return 2 } return (choicesLength < 4) ? 2 : 3 } i18n = new VueI18n({ locale: 'ru', messages: { ru: { car: '0 машин | {n} машина | {n} машины | {n} машин' } } }) vm = new Vue({ i18n }) assert.strictEqual(vm.$tc('car', 0), '0 машин') assert.strictEqual(vm.$tc('car', 1), '1 машина') assert.strictEqual(vm.$tc('car', 2), '2 машины') assert.strictEqual(vm.$tc('car', 4), '4 машины') assert.strictEqual(vm.$tc('car', 12), '12 машин') assert.strictEqual(vm.$tc('car', 21), '21 машина') // Set the default implementation back VueI18n.prototype.getChoiceIndex = defaultImpl }) }) describe('#468', () => { it('should be existed', done => { assert(vm.$te('hello world') === true) done() }) }) describe('#484', () => { it('passes path to the formatter', () => { const testPath = 'test.deep.message' i18n = new VueI18n({ locale: 'en', messages: { en: { test: { deep: { message: 'Hello!' } } } }, formatter: { interpolate (message, values, path) { assert.strictEqual(path, testPath) return null // pass the case to the default formatter } } }) assert.strictEqual(i18n.t(testPath), 'Hello!') }) }) describe('#478', () => { it('should be translated', () => { const res = vm.$t('message.linkHelloName', { name: 'World {text}' }) assert.strictEqual(res, 'Hello World {text}') }) it('should be translated', () => { const res = vm.$t('message.linkHelloName', { name: 'World {text}', text: 'something' }) assert.strictEqual(res, 'Hello World {text}') }) it('should be translated', () => { const res = vm.$t('message.linkLinkHelloName', { name: 'World {text}', text: 'something' }) assert.strictEqual(res, 'Hello World {text}') }) }) describe('#515', () => { it('$te should return true for empty string', () => { assert.strictEqual(vm.$te('message.empty'), true) }) }) describe('#879', () => { it('$t should not throw when invoked on a destroyed component', async () => { const vm = new Vue({ i18n: new VueI18n({ locale: 'en' }), methods: { test: async function () { // Long running async method that terminates after the component is destroyed. await new Promise((resolve) => setTimeout(resolve, 50)) this.$t('anything') } } }) const promise = vm.test() // invocation before being destroyed vm.$destroy() await promise // should not throw }) }) describe('#892)', () => { it('should not call "componentInstanceCreatedListener" on not creating local instance', () => { const componentInstanceCreatedListener = sinon.spy() new Vue({ i18n: new VueI18n({ locale: 'en', componentInstanceCreatedListener }), components: { child: { render (h) { return h('p', ['hello child']) } } }, render (h) { return h('div', [ h('child', { ref: 'child' }) ]) } }).$mount() assert(componentInstanceCreatedListener.called === false) }) it('should call "componentInstanceCreatedListener" on creating local instance', () => { const componentInstanceCreatedListener = sinon.spy() const i18n = new VueI18n({ locale: 'en', componentInstanceCreatedListener }) new Vue({ i18n, components: { child: { i18n: { locale: 'ja' }, render (h) { return h('p', ['hello child']) } } }, render (h) { return h('div', [ h('child', { ref: 'child' }) ]) } }).$mount() assert(componentInstanceCreatedListener.calledOnce === true) console.info(componentInstanceCreatedListener.args) assert(componentInstanceCreatedListener.args[0][0] instanceof VueI18n) // new instance assert.strictEqual(componentInstanceCreatedListener.args[0][1], i18n) }) }) describe('#996', () => { it('should merge __i18n and i18n', done => { const Component = Vue.extend({ __i18n: [JSON.stringify({ en: { custom: 'custom block!' } })], render (h) { return h('p') } }) const vm = new Component({ i18n: new VueI18n({ locale: 'en', messages: { en: { another: 'another block!' } } }) }).$mount() Vue.nextTick().then(() => { assert.strictEqual(vm.$t('another'), 'another block!') assert.strictEqual(vm.$t('custom'), 'custom block!') }).then(done) .catch(console.error) }) }) describe('#1380', () => { it('should be translated', done => { vm = new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { 'link-with-slash': '@:key/with/slash', 'key/with/slash': 'should be translated' } } }), render (h) { return h('div', [ h('p', { ref: 'text' }, [this.$t('link-with-slash')]) ]) } }).$mount() Vue.nextTick().then(() => { assert.strictEqual(vm.$refs.text.textContent, 'should be translated') }).then(done) }) }) describe('#1488', () => { it('should be translated', () => { assert.strictEqual(vm.$i18n.t('message.linkColon'), messages.en['colon:hello']) assert.strictEqual(vm.$i18n.t('message.linkPipe'), messages.en['pipe|hello']) }) }) describe('#1308', () => { it('should be translated', () => { const i18n = new VueI18n({ locale: 'en', messages: { en: { 'address': 'home Address', 'snakeAddress': '@.snakeCase:(address)' } }, name: 'test', modifiers: { snakeCase: str => str.split(' ').join('-') }, }) assert.strictEqual(i18n.t('snakeAddress'), 'home-Address') }) }) }) ================================================ FILE: test/unit/message.test.js ================================================ import messages from './fixture/index' describe('message', () => { let el let i18n let orgEnLocale let orgJaLocaleMessage const expectEnLocale = 'the world updated' const expectJaLocaleMessage = { message: { hello: 'ザ・世界 -> メイド・イン・ヘブン' } } beforeEach(() => { i18n = new VueI18n({ locale: 'en', messages }) orgEnLocale = i18n.getLocaleMessage('en').message.hello orgJaLocaleMessage = i18n.getLocaleMessage('ja') el = document.createElement('div') document.body.appendChild(el) }) afterEach(() => { messages.en.message.hello = orgEnLocale i18n.setLocaleMessage('en', messages.en) i18n.setLocaleMessage('ja', orgJaLocaleMessage) }) describe('messages', () => { it('should be workd', () => { assert.deepEqual(messages, i18n.messages) }) }) describe('getLocaleMessage / setLocaleMessage', () => { it('should be worked', done => { const vm = new Vue({ i18n, render (h) { return h('p', { ref: 'text' }, [this.$t('message.hello')]) } }).$mount(el) const { text } = vm.$refs Vue.nextTick().then(() => { assert.strictEqual(text.textContent, messages.en.message.hello) // hot reload (set reactivity messages) messages.en.message.hello = expectEnLocale i18n.setLocaleMessage('en', messages.en) }).then(() => { assert.strictEqual(text.textContent, expectEnLocale) // upade locale i18n.setLocaleMessage('ja', expectJaLocaleMessage) i18n.locale = 'ja' }).then(() => { assert.strictEqual(text.textContent, expectJaLocaleMessage.message.hello) }).then(done) }) }) describe('mergeLocaleMessage', () => { it('should be merged', () => { i18n = new VueI18n({ locale: 'en', messages: { en: { foo: 'bar' }, ja: { foo: 'バー' } } }) i18n.mergeLocaleMessage('en', { bar: 'foo' }) assert.deepEqual({ foo: 'bar', bar: 'foo' }, i18n.getLocaleMessage('en')) }) }) it('Should be merged and notified if the target is empty', (done) => { const i18n = new VueI18n({ locale: 'ru', messages: { ru: {} } }) const uw = i18n._vm.$watch('messages.ru.foo', (newVal, oldVal, o) => { assert.equal(newVal, 'бар') uw() done() }) i18n.mergeLocaleMessage('ru', { foo: 'бар' }) assert.deepEqual({ foo: 'бар' }, i18n.getLocaleMessage('ru')) }) }) ================================================ FILE: test/unit/message_function.test.js ================================================ describe('message function', () => { describe('simple', () => { it('should be worked', () => { i18n = new VueI18n({ locale: 'ja', fallbackLocale: 'en', messages: { en: { hello: (ctx) => 'hello' }, ja: { hello: (ctx) => 'こんにちは!' } } }) assert.strictEqual(i18n.t('hello'), 'こんにちは!') }) }) describe('list argument', () => { it('should be worked', () => { i18n = new VueI18n({ locale: 'ja', fallbackLocale: 'en', messages: { en: { hello: (ctx) => `hello, ${ctx.list(0)}!` }, ja: { hello: (ctx) => `こんにちは、${ctx.list(0)}!` } } }) assert.strictEqual(i18n.t('hello', ['kazupon']), 'こんにちは、kazupon!') }) }) describe('named argument', () => { it('should be worked', () => { i18n = new VueI18n({ locale: 'ja', fallbackLocale: 'en', messages: { en: { hello: (ctx) => `hello, ${ctx.named('name')}!` }, ja: { hello: (ctx) => `こんにちは、${ctx.named('name')}!` } } }) assert.strictEqual(i18n.t('hello', { name: 'kazupon' }), 'こんにちは、kazupon!') }) }) it('receives formatter, values, path, messages, locale', () => { i18n = new VueI18n({ locale: 'ja', fallbackLocale: 'en', messages: { en: { hello: (ctx) => ctx.formatter.interpolate(`hey {x} {y}`, ctx.values, ctx.path).join(''), }, ja: { hello: (ctx) => ctx.formatter.interpolate(`hey {x} {y}`, ctx.values, ctx.path).join('') + ` ${ctx.messages[ctx.locale].world}`, world: 'w0r1d' } } }) assert.strictEqual(i18n.t('hello', { x: 'foo', y: 'bar' }), 'hey foo bar w0r1d') }) it('receives linked function for resolving linked messages', () => { i18n = new VueI18n({ locale: 'en', messages: { en: { hello: (ctx) => `hello ${ctx.linked('foo')}`, foo: 'world' } } }) assert.strictEqual(i18n.t('hello'), 'hello world') }) }) ================================================ FILE: test/unit/missing.test.js ================================================ describe('missing', () => { describe('via i18n instance API', () => { it('should be handled translate missing', done => { const i18n = new VueI18n({ locale: 'en', missing: (locale, key, vm) => { assert.strictEqual('en', locale) assert.strictEqual('foo.bar.buz', key) assert(vm === null) done() } }) i18n.t('foo.bar.buz') }) }) describe('via vue instance', () => { it('should be handled translate missing', done => { const vm = new Vue({ i18n: new VueI18n({ locale: 'en', missing: (locale, key, instance) => { assert.strictEqual('en', locale) assert.strictEqual('foo.bar.buz', key) assert(vm === instance) done() } }) }) vm.$t('foo.bar.buz') }) }) describe('i18n missing getter/setter', () => { it('should be worked', done => { const missing = (locale, key) => { assert(false) } const i18n = new VueI18n({ locale: 'en', missing }) assert.strictEqual(missing, i18n.missing) i18n.missing = (locale, key, vm) => { done() } i18n.t('foo.bar.buz') }) }) describe('i18n missing values', () => { it('should receive the values for interpolation', done => { const testValues = { foo: 'bar', num: 1234 } const missing = (locale, key, vm, values) => { assert.strictEqual('en', locale) assert.strictEqual('cannot.find', key) // `values` is normalized to be an array. assert.strictEqual('bar', values[0].foo) assert.strictEqual(1234, values[0].num) done() } const i18n = new VueI18n({ locale: 'en', missing }) i18n.t('cannot.find', testValues) }) }) describe('missing handler return', () => { it('should be returned missing handler', done => { const i18n = new VueI18n({ locale: 'en', missing: (locale, key, vm) => { return key } }) assert.strictEqual(i18n.t('foo.bar.buz'), 'foo.bar.buz') done() }) }) }) ================================================ FILE: test/unit/mixin.test.js ================================================ import defineMixin from '../../src/mixin' describe('mixin', () => { describe('beforeCreate', () => { describe('invalid i18n option', () => { it('should be warned', () => { const spy = sinon.spy(console, 'warn') // called from Vue core new Vue({ i18n: 1 }) assert(spy.notCalled === false) assert(spy.callCount === 1) spy.restore() }) }) }) describe('beforeDestroy', () => { describe('not assign VueI18n instance', () => { it('should be succeeded', () => { assert(defineMixin().beforeDestroy() === undefined) }) }) }) }) ================================================ FILE: test/unit/number.test.js ================================================ import numberFormats from './fixture/number' function convertToCharCodes (str) { const codes = [] for (let i = 0; i < str.length; i++) { codes.push(str.charCodeAt(i)) } return codes } const desc = VueI18n.availabilities.numberFormat ? describe : describe.skip desc('number format', () => { describe('numberFormats', () => { it('should be worked', done => { const i18n = new VueI18n({ locale: 'en-US', numberFormats }) Vue.nextTick().then(() => { assert.deepEqual(numberFormats, i18n.numberFormats) }).then(done) }) }) describe('getNumberFormat / setNumberFormat', () => { it('should be worked', done => { const i18n = new VueI18n({ locale: 'en-US', numberFormats }) const el = document.createElement('div') document.body.appendChild(el) const money = 101 const vm = new Vue({ i18n, render (h) { return h('p', { ref: 'text' }, [this.$n(money, 'currency')]) } }).$mount(el) const { text } = vm.$refs const zhFormat = { currency: { style: 'currency', currency: 'CNY', currencyDisplay: 'name' } } Vue.nextTick().then(() => { assert.strictEqual(text.textContent, '$101.00') i18n.setNumberFormat('zh-CN', zhFormat) assert.deepEqual(i18n.getNumberFormat('zh-CN'), zhFormat) i18n.locale = 'zh-CN' }).then(() => { // NOTE: avoid webkit (safari/phantomjs) & Intl polyfill wired localization... if (isChrome) { const actual = [49, 48, 49, 46, 48, 48, 20154, 27665, 24065] // 101.00人民币 const target = convertToCharCodes(text.textContent) assert.deepEqual(target, actual) } }).then(done) }) }) describe('mergeNumberFormat', () => { it('should be merged', () => { const i18n = new VueI18n({ locale: 'ja-JP', numberFormats }) const percent = { style: 'percent' } i18n.mergeNumberFormat('en-US', { percent }) assert.deepEqual(percent, i18n.getNumberFormat('en-US').percent) }) }) describe('fallback', () => { it('should be fallback', done => { const i18n = new VueI18n({ locale: 'en-uk', fallbackLocale: ['de', 'en-us'], numberFormats: { de: { currency: { currency: 'EUR', style: 'currency', minimumFractionDigits: 2, maximumFractionDigits: 2 } } } }) const el = document.createElement('div') document.body.appendChild(el) const money = 101 const vm = new Vue({ i18n, render (h) { return h('p', { ref: 'text' }, [this.$n(money, 'currency')]) } }).$mount(el) const { text } = vm.$refs Vue.nextTick().then(() => { assert.strictEqual(text.textContent, '101,00 €') }).then(done) }) }) }) ================================================ FILE: test/unit/number_component.test.js ================================================ import numberFormats from './fixture/number' const desc = VueI18n.availabilities.numberFormat ? describe : describe.skip desc('number custom formatting', () => { let i18n let value beforeEach(() => { i18n = new VueI18n({ locale: 'en-US', fallbackLocale: 'ja-JP', numberFormats }) value = 10100 }) describe('basic', () => { it('should be formatted', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.textContent, '10,100') }).then(done) }) }) describe('format', () => { describe('as string property', () => { it('should be formatted', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value, format: 'currency' } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.textContent, '$10,100.00') }).then(done) }) }) describe('as object property', () => { it('should be formatted', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value, format: { key: 'currency' } } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.textContent, '$10,100.00') }).then(done) }) }) }) describe('locale', () => { it('should be formatted', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value, format: 'currency', locale: 'ja-JP' } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.textContent, '¥10,100') }).then(done) }) }) describe('tag', () => { it('should default to span', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.outerHTML, '10,100') }).then(done) }) it('should be formatted', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value, tag: 'p' } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.outerHTML, '

10,100

') }).then(done) }) it('of value true defaults to span', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value, tag: true } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.outerHTML, '10,100') }).then(done) }) it('of value false does not apply a root container', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value, tag: false } }) }, }) Vue.nextTick().then(() => { console.log('\n\n\n\n\n\n\n', vm.$el, '\n\n', vm.$el.nodeType) assert.strictEqual(vm.$el.nodeType, Node.TEXT_NODE) assert.strictEqual(vm.$el.data, '10,100') }).then(done) }) }) describe('explicit options', () => { describe('without key', () => { it('should be formatted', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value, format: { style: 'currency', currency: 'JPY' } } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.textContent, '¥10,100') }).then(done) }) it('should respect other number options', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value, format: { style: 'currency', currency: 'EUR', currencyDisplay: 'code' } } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.textContent, 'EUR 10,100.00') }).then(done) }) }) describe('with key', () => { it('should be formatted', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value, format: { key: 'currency', currency: 'JPY' } } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.textContent, '¥10,100') }).then(done) }) it('should respect other number options', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value, format: { key: 'currency', currency: 'EUR', currencyDisplay: 'code' } } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.textContent, 'EUR 10,100.00') }).then(done) }) }) }) describe('partial formatting', () => { it('should be formatted', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value }, scopedSlots: { integer: props => h('span', props.integer), group: props => h('p', props.group) } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, '10

,

100') }).then(done) }) it('should pass part index as scoped prop', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value: 1000000, format: 'currency' }, scopedSlots: { currency: props => h('span', new Array(3).fill(props.currency).join('')), group: props => h('p', { staticClass: props.index }, props.group) } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, '$$$1

,

000

,

000.00') }).then(done) }) it('should pass parts as scoped prop', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value: -12 }, scopedSlots: { integer: props => h('span', { staticClass: props.parts.find(part => part.type === 'minusSign') ? 'red' : '' }, props.integer) } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, '-12') }).then(done) }) it('should ignore non-present scoped slot', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value }, scopedSlots: { currency: props => h('span', props.currency) } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, '10,100') }).then(done) }) it('should ignore default scoped slot', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value }, scopedSlots: { default: props => h('span', props.integer) } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.innerHTML, '10,100') }).then(done) }) }) describe('fallback', () => { it('should be formatted', done => { const vm = new Vue({ i18n, el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value: 0.9, format: 'percent' } }) }, }) Vue.nextTick().then(() => { assert.strictEqual(vm.$el.textContent, '90%') }).then(done) }) }) describe('change number format runtime', () => { it('should be changed', done => { const i18n = new VueI18n({ locale: 'en-US', numberFormats }) const el = document.createElement('div') document.body.appendChild(el) const money = 101 const vm = new Vue({ i18n, render (h) { return h('p', { ref: 'text' }, [this.$n(money, 'currency')]) } }).$mount(el) const { text } = vm.$refs const otherEnFormat = { currency: { style: 'currency', currency: 'CZK', currencyDisplay: 'name' } } Vue.nextTick().then(() => { assert.strictEqual(text.textContent, '$101.00') i18n.setNumberFormat('en-US', otherEnFormat) }).then(() => { assert.strictEqual(text.textContent, '101.00 Czech korunas') }).then(done) }) }) describe('warnning in render', () => { it('should be warned', () => { const spy = sinon.spy(console, 'warn') new Vue({ el: document.createElement('div'), render (h) { return h('i18n-n', { props: { value } }) }, }) assert(spy.notCalled === false) assert(spy.callCount === 1) spy.restore() }) }) }) ================================================ FILE: test/unit/path.test.js ================================================ import I18nPath from '../../src/path' describe('path', () => { const path = new I18nPath() describe('primitive', () => { it('should get path value', () => { assert.strictEqual(path.getPathValue({ a: { b: 1 } }, 'a.b'), 1) }) }) describe('whitespace', () => { it('should get value if it contains space 0x20', () => { const val = path.getPathValue({ 'a c': 1 }, 'a c') assert.strictEqual(val, 1) }) it('should return null if it contains whitespace chars except space 0x20', () => { const val = path.getPathValue({ 'a\tc': 1 }, 'a\tc') assert.strictEqual(val, null) }) }) describe('object', () => { it('should get path value', () => { const val = path.getPathValue({ a: { b: 1 } }, 'a') assert.strictEqual(val.b, 1) }) it('should accept space 0x20 as keypath', () => { const val = path.getPathValue({ a: { 'b c d': 1 } }, 'a.b c d') assert.strictEqual(val, 1) }) }) describe('number key in object', () => { it('should get path value', () => { assert.strictEqual( path.getPathValue({ errors: { '1': 'error number 1' } }, 'errors[1]'), 'error number 1' ) }) }) describe('array index path', () => { it('should get value', () => { assert.strictEqual( path.getPathValue({ errors: ['error number 0'] }, 'errors[0]'), 'error number 0' ) }) }) describe('array path', () => { it('should get path value', () => { assert.strictEqual( path.getPathValue({ errors: ['error number 0'] }, 'errors')[0], 'error number 0' ) }) }) describe('not found', () => { it('should not get null', () => { assert.strictEqual(path.getPathValue({}, 'a.b'), null) }) }) describe('obj: primitive', () => { it('should not get null', () => { assert.strictEqual(path.getPathValue(10, 'a.b'), null) }) }) describe('obj: null', () => { it('should not get null', () => { assert.strictEqual(path.getPathValue(null, 'a.b'), null) }) }) describe('Blanket: term', () => { it('should not get null', () => { assert.strictEqual(path.getPathValue({}, 'a.b.c[]'), null) }) }) describe('Blanket: middle', () => { it('should not get null', () => { assert.strictEqual(path.getPathValue({}, 'a.b.c[]d'), null) }) }) describe('obj: null child', () => { it('should return null if parent is null', () => { assert.strictEqual(path.getPathValue({ a: null }, 'a.b'), null) }) }) }) ================================================ FILE: test/unit/post_translation.test.js ================================================ describe('postTranslation option', () => { let i18n beforeEach(() => { i18n = new VueI18n({ locale: 'en', postTranslation: str => str.trim(), messages: { en: { hello: ' hello world! ', apple: ' no apples | one apple | {count} apples ' } } }) }) describe('t', () => { it('should be hooked', () => { assert.strictEqual(i18n.t('hello'), 'hello world!') }) }) describe('$t', () => { it('should be hooked', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$t('hello'), 'hello world!') }) }) describe('tc', () => { it('should be hooked', () => { assert.strictEqual(i18n.tc('apple', 0), 'no apples') }) }) describe('$tc', () => { it('should be hooked', () => { const vm = new Vue({ i18n }) assert.strictEqual(vm.$tc('apple', 0), 'no apples') }) }) describe('postTranslation option', () => { describe('default', () => { it('should be undefined', () => { i18n = new VueI18n({ locale: 'en' }) assert.strictEqual(i18n.postTranslation, null) }) }) describe('set hook', () => { it('should be hooked', () => { i18n = new VueI18n({ locale: 'en', messages: { en: { hello: ' hello world! ' } } }) const vm = new Vue({ i18n }) assert.strictEqual(vm.$t('hello'), ' hello world! ') // set! i18n.postTranslation = str => str.trim() assert.strictEqual(vm.$t('hello'), 'hello world!') }) }) }) }) ================================================ FILE: test/unit/silent.test.js ================================================ import dateTimeFormats from './fixture/datetime' import numberFormats from './fixture/number' describe('silent', () => { let spy beforeEach(() => { spy = sinon.spy(console, 'warn') }) afterEach(() => { spy.restore() }) describe('silentTranslationWarn', () => { describe('boolean', () => { it('should be suppressed translate warnings', () => { const warningRegex = /Cannot translate the value of keypath 'foo.bar.buz'. Use the value of keypath as default./ const vm = new Vue({ i18n: new VueI18n({ locale: 'en', silentTranslationWarn: true, messages: { en: { who: 'root' }, ja: { who: 'ルート' } } }) }) vm.$t('foo.bar.buz') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === false) // change vm.$i18n.silentTranslationWarn = false vm.$t('foo.bar.buz') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === true) }) }) describe('Regex', () => { it('should be suppressed translate warnings', () => { const warningRegex = /Cannot translate the value of keypath .*\. Use the value of keypath as default./ const vm = new Vue({ i18n: new VueI18n({ locale: 'en', silentTranslationWarn: true, messages: { en: { who: 'root' }, ja: { who: 'ルート' } } }) }) vm.$t('foo.bar.buz') vm.$t('who.bar') vm.$t('who.bar.buz') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === false) // change to boolean vm.$i18n.silentTranslationWarn = /^foo\..*|who\.bar$/ vm.$t('foo.bar.buz') vm.$t('who.bar') vm.$t('who.bar.buz') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === true) }) }) }) describe('silentFallbackWarn', () => { let i18n beforeEach(() => { i18n = new VueI18n({ locale: 'hu', fallbackLocale: 'en', silentFallbackWarn: true, messages: { en: { winner: 'winner' }, hu: { chickenDinner: 'csirkevacsora' } } }) }) describe('boolean', () => { it('should suppress `Fall back to ${fallback} locale` warnings', () => { const vm = new Vue({ i18n }) const warningRegex = /Fall back to .* 'en' locale./ vm.$t('winner') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === false) vm.$i18n.silentFallbackWarn = false vm.$t('winner') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === true) }) it('should suppress `Fall back to root locale` warnings.', () => { const el = document.createElement('div') const root = new Vue({ i18n, components: { subComponent: { i18n: { messages: { hu: { name: 'Név' } } }, render (h) { return h('p') } } }, render (h) { return h('sub-component') } }).$mount(el) const vm = root.$children[0] const warningRegex = /Fall back to .* root locale./ vm.$t('chickenDinner') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === false) vm.$i18n.silentFallbackWarn = false vm.$t('chickenDinner') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === true) }) describe('if first try is null or undefined,', () => { it('should suppress `not a string` warnings for fallback to fallbackLocale.', () => { const vm = new Vue({ i18n }) const warningRegex = /Value of .* is not a string./ vm.$t('winner') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === false) vm.$i18n.silentFallbackWarn = false vm.$t('winner') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === true) }) it('should supress `not a string` warnings for fallback to root.', () => { const el = document.createElement('div') const root = new Vue({ i18n, components: { subComponent: { i18n: { messages: { hu: { name: 'Név' } } }, render (h) { return h('p') } } }, render (h) { return h('sub-component') } }).$mount(el) const vm = root.$children[0] const warningRegex = /Value of .* is not a string./ vm.$t('chickenDinner') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === false) vm.$i18n.silentFallbackWarn = false vm.$t('chickenDinner') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === true) }) }) describe('if first try is not null, undefined, array, plain object or string,', () => { it('should suppress `not a string` warnings for fallback to fallbackLocale.', () => { const vm = new Vue({ i18n: new VueI18n({ locale: 'hu', fallbackLocale: 'en', silentFallbackWarn: true, messages: { en: { winner: 'winner' }, hu: { winner: true } // translation value is boolean } }) }) const warningRegex = /Value of .* is not a string./ vm.$t('winner') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === false) vm.$i18n.silentFallbackWarn = false vm.$t('winner') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === true) }) it('should supress `not a string` warnings for fallback to root.', () => { const el = document.createElement('div') const root = new Vue({ i18n, components: { subComponent: { i18n: { messages: { hu: { chickenDinner: 11 } } }, // translation value is number render (h) { return h('p') } } }, render (h) { return h('sub-component') } }).$mount(el) const vm = root.$children[0] const warningRegex = /Value of .* is not a string./ vm.$t('chickenDinner') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === false) vm.$i18n.silentFallbackWarn = false vm.$t('chickenDinner') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === true) }) }) it('should not suppress `not a string` warnings when no further fallback is possible.', () => { const vm = new Vue({ i18n }) const warningRegex = /Value of .* is not a string./ vm.$t('loser') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === true) }) }) describe('Regexp', () => { it('should be suppressed translate warnings', () => { const el = document.createElement('div') const root = new Vue({ i18n, components: { subComponent: { i18n: { messages: { hu: { name: 'Név' } } }, render (h) { return h('p') } } }, render (h) { return h('sub-component') } }).$mount(el) const vm = root.$children[0] const warningRegex = /Fall back to .*\./ vm.$t('chickenDinner') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === false) // change to boolean vm.$i18n.silentFallbackWarn = /chic.*/ vm.$t('chickenDinner') assert(spy.getCalls().some(call => call.args[0].match(warningRegex)) === false) }) }) describe('datetime/number format', () => { it('should be suppressed translate warnings', () => { const el = document.createElement('div') const root = new Vue({ i18n: new VueI18n({ locale: 'en-US', fallbackLocale: 'ja-JP', silentFallbackWarn: true, dateTimeFormats, numberFormats }), components: { subComponent: { i18n: { dateTimeFormats: {}, numberFormats: {} }, render (h) { return h('p') } } }, render (h) { return h('sub-component') } }).$mount(el) const vm = root.$children[0] const warningDateTimeRegex = /Fall back to .* datetime formats\./ const warningNumberRegex = /Fall back to .* number formats\./ vm.$d(Date.now(), 'long') vm.$n(10, 'numeric') assert(spy.getCalls().some(call => call.args[0].match(warningDateTimeRegex)) === false) assert(spy.getCalls().some(call => call.args[0].match(warningNumberRegex)) === false) // change vm.$i18n.silentFallbackWarn = false vm.$d(Date.now(), 'long') vm.$n(10, 'numeric') assert(spy.getCalls().some(call => call.args[0].match(warningDateTimeRegex)) === true) assert(spy.getCalls().some(call => call.args[0].match(warningNumberRegex)) === true) }) }) }) }) ================================================ FILE: test/unit/warn_html_in_message.test.js ================================================ describe('warnHtmlInMessage', () => { let spyWarn let spyError beforeEach(() => { spyWarn = sinon.spy(console, 'warn') spyError = sinon.spy(console, 'error') }) afterEach(() => { spyWarn.restore() spyError.restore() }) describe('constructor option', () => { it('should be worked', () => { const messages = { en: { message: { foo: { buz: '

buz

', hello: 'hello' }, bar: [1, { buz: '

buz

' }], buz: 22 } }, ja: { message: '

こんにちは

' } } // `off` new VueI18n({ warnHtmlInMessage: 'off', messages }) assert(spyWarn.callCount === 0) assert(spyError.callCount === 0) // `warn` new VueI18n({ warnHtmlInMessage: 'warn', messages }) assert(spyWarn.callCount === 3) assert(spyError.callCount === 0) // `error` new VueI18n({ warnHtmlInMessage: 'error', messages }) assert(spyWarn.callCount === 3) assert(spyError.callCount === 3) }) }) describe('property', () => { it('should be worked', () => { const messages = { en: { message: { foo: { buz: '

buz

' }, bar: [1, '

bar

'], buz: 22 } }, ja: { message: '

こんにちは

' } } const i18n = new VueI18n({ warnHtmlInMessage: 'off', messages }) // `warn` i18n.warnHtmlInMessage = 'warn' assert(spyWarn.callCount === 3) assert(spyError.callCount === 0) // `error` i18n.warnHtmlInMessage = 'error' assert(spyWarn.callCount === 3) assert(spyError.callCount === 3) // `off` i18n.warnHtmlInMessage = 'off' assert(spyWarn.callCount === 3) assert(spyError.callCount === 3) }) }) describe('setLocaleMessage', () => { it('should be worked', () => { const i18n = new VueI18n({ warnHtmlInMessage: 'warn', messages: { en: {}, ja: {} } }) i18n.setLocaleMessage('en', { hello: '

hello

' }) assert(spyWarn.callCount === 1) assert(spyError.callCount === 0) i18n.warnHtmlInMessage = 'error' i18n.setLocaleMessage('ja', { hello: '

こんにちは

' }) assert(spyWarn.callCount === 1) assert(spyError.callCount === 2) i18n.warnHtmlInMessage = 'off' assert(spyWarn.callCount === 1) assert(spyError.callCount === 2) }) }) describe('mergeLocaleMessage', () => { it('should be worked', () => { const i18n = new VueI18n({ warnHtmlInMessage: 'warn', messages: { en: {}, ja: {} } }) i18n.mergeLocaleMessage('en', { hello: '

hello

' }) assert(spyWarn.callCount === 1) assert(spyError.callCount === 0) i18n.warnHtmlInMessage = 'error' i18n.mergeLocaleMessage('ja', { hello: '

こんにちは

' }) assert(spyWarn.callCount === 1) assert(spyError.callCount === 2) i18n.warnHtmlInMessage = 'off' assert(spyWarn.callCount === 1) assert(spyError.callCount === 2) }) }) }) ================================================ FILE: types/.eslintrc.js ================================================ module.exports = { root: true, parserOptions: { ecmaVersion: 2018, }, extends: [ 'plugin:@typescript-eslint/eslint-recommended', ], plugins: [ '@typescript-eslint', ], parser: '@typescript-eslint/parser', parserOptions: { sourceType: 'module', }, rules: { 'object-curly-spacing': ['error', 'always'], 'no-multiple-empty-lines': ['error', { max: 2, maxBOF: 1 }] } } ================================================ FILE: types/index.d.ts ================================================ export type Path = VueI18n.Path; export type Locale = VueI18n.Locale; export type FallbackLocale = VueI18n.FallbackLocale; export type Values = VueI18n.Values; export type Choice = VueI18n.Choice; export type MessageContext = VueI18n.MessageContext; export type MessageFunction = VueI18n.MessageFunction; export type LocaleMessage = VueI18n.LocaleMessage; export type LocaleMessageObject = VueI18n.LocaleMessageObject; export type LocaleMessageArray = VueI18n.LocaleMessageArray; export type LocaleMessages = VueI18n.LocaleMessages; export type TranslateResult = VueI18n.TranslateResult; export type DateTimeFormatOptions = VueI18n.DateTimeFormatOptions; export type DateTimeFormat = VueI18n.DateTimeFormat; export type DateTimeFormats = VueI18n.DateTimeFormats; export type DateTimeFormatResult = VueI18n.DateTimeFormatResult; export type NumberFormatOptions = VueI18n.NumberFormatOptions; export type NumberFormat = VueI18n.NumberFormat; export type NumberFormats = VueI18n.NumberFormats; export type NumberFormatResult = VueI18n.NumberFormatResult; export type NumberFormatToPartsResult = VueI18n.NumberFormatToPartsResult; export type WarnHtmlInMessageLevel = VueI18n.WarnHtmlInMessageLevel; export type Formatter = VueI18n.Formatter; export type MissingHandler = VueI18n.MissingHandler; export type PostTranslationHandler = VueI18n.PostTranslationHandler; export type IntlAvailability = VueI18n.IntlAvailability; export type I18nOptions = VueI18n.I18nOptions; export type PluignOptions = VueI18n.PluignOptions import Vue, { PluginFunction } from 'vue'; declare namespace VueI18n { type Path = string; type Locale = string; type FallbackLocale = string | string[] | false | { [locale: string]: string[] } type Values = any[] | { [key: string]: any }; type Choice = number; interface MessageContext { list(index: number): unknown named(key: string): unknown linked(key: string): VueI18n.TranslateResult values: any path: string formatter: Formatter messages: LocaleMessages locale: Locale } type MessageFunction = (ctx: MessageContext) => string; type LocaleMessage = string | MessageFunction | LocaleMessageObject | LocaleMessageArray; interface LocaleMessageObject { [key: string]: LocaleMessage; } interface LocaleMessageArray { [index: number]: LocaleMessage; } interface LocaleMessages { [key: string]: LocaleMessageObject; } type TranslateResult = string | LocaleMessages; type LocaleMatcher = 'lookup' | 'best fit'; type FormatMatcher = 'basic' | 'best fit'; type DateTimeHumanReadable = 'long' | 'short' | 'narrow'; type DateTimeDigital = 'numeric' | '2-digit'; interface SpecificDateTimeFormatOptions extends Intl.DateTimeFormatOptions { year?: DateTimeDigital; month?: DateTimeDigital | DateTimeHumanReadable; day?: DateTimeDigital; hour?: DateTimeDigital; minute?: DateTimeDigital; second?: DateTimeDigital; weekday?: DateTimeHumanReadable; era?: DateTimeHumanReadable; timeZoneName?: 'long' | 'short'; localeMatcher?: LocaleMatcher; formatMatcher?: FormatMatcher; } type DateTimeFormatOptions = Intl.DateTimeFormatOptions | SpecificDateTimeFormatOptions; interface DateTimeFormat { [key: string]: DateTimeFormatOptions; } interface DateTimeFormats { [locale: string]: DateTimeFormat; } type DateTimeFormatResult = string; type CurrencyDisplay = 'symbol' | 'code' | 'name'; interface SpecificNumberFormatOptions extends Intl.NumberFormatOptions { style?: 'decimal' | 'percent'; currency?: string; currencyDisplay?: CurrencyDisplay; localeMatcher?: LocaleMatcher; formatMatcher?: FormatMatcher; } interface CurrencyNumberFormatOptions extends Intl.NumberFormatOptions { style: 'currency'; currency: string; // Obligatory if style is 'currency' currencyDisplay?: CurrencyDisplay; localeMatcher?: LocaleMatcher; formatMatcher?: FormatMatcher; } type NumberFormatOptions = Intl.NumberFormatOptions | SpecificNumberFormatOptions | CurrencyNumberFormatOptions; interface NumberFormat { [key: string]: NumberFormatOptions; } interface NumberFormats { [locale: string]: NumberFormat; } type NumberFormatResult = string; type PluralizationRulesMap = { /** * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)` * @param choicesLength {number} an overall amount of available choices * @returns a final choice index */ [lang: string]: (choice: number, choicesLength: number) => number; }; type Modifiers = { [key: string]: (str : string) => string }; type FormattedNumberPartType = 'currency' | 'decimal' | 'fraction' | 'group' | 'infinity' | 'integer' | 'literal' | 'minusSign' | 'nan' | 'plusSign' | 'percentSign'; type WarnHtmlInMessageLevel = 'off' | 'warn' | 'error'; interface FormattedNumberPart { type: FormattedNumberPartType; value: string; } interface NumberFormatToPartsResult { [index: number]: FormattedNumberPart; } interface Formatter { interpolate(message: string, values: Values | undefined, path: string): (any[] | null); } type MissingHandler = (locale: Locale, key: Path, vm: Vue | null, values: any) => string | void; type PostTranslationHandler = (str: string, key?: string) => string; type ComponentInstanceCreatedListener = (newVm: VueI18n & IVueI18n, rootVm: VueI18n & IVueI18n) => void; interface IntlAvailability { dateTimeFormat: boolean; numberFormat: boolean; } // tslint:disable-next-line:interface-name interface I18nOptions { locale?: Locale; fallbackLocale?: FallbackLocale; messages?: LocaleMessages; dateTimeFormats?: DateTimeFormats; numberFormats?: NumberFormats; formatter?: Formatter; modifiers?: Modifiers, missing?: MissingHandler; fallbackRoot?: boolean; fallbackRootWithEmptyString?: boolean, formatFallbackMessages?: boolean; sync?: boolean; silentTranslationWarn?: boolean | RegExp; silentFallbackWarn?: boolean | RegExp; preserveDirectiveContent?: boolean; pluralizationRules?: PluralizationRulesMap; warnHtmlInMessage?: WarnHtmlInMessageLevel; sharedMessages?: LocaleMessages; postTranslation?: PostTranslationHandler; componentInstanceCreatedListener?: ComponentInstanceCreatedListener; escapeParameterHtml?: boolean; } type PluignOptions = { bridge?: boolean } } export declare interface IVueI18n { readonly messages: VueI18n.LocaleMessages; readonly dateTimeFormats: VueI18n.DateTimeFormats; readonly numberFormats: VueI18n.NumberFormats; locale: VueI18n.Locale; fallbackLocale: VueI18n.FallbackLocale; missing: VueI18n.MissingHandler; formatter: VueI18n.Formatter; formatFallbackMessages: boolean; silentTranslationWarn: boolean | RegExp; silentFallbackWarn: boolean | RegExp; preserveDirectiveContent: boolean; sync: boolean; pluralizationRules: VueI18n.PluralizationRulesMap; warnHtmlInMessage: VueI18n.WarnHtmlInMessageLevel; postTranslation: VueI18n.PostTranslationHandler; t(key: VueI18n.Path, values?: VueI18n.Values): VueI18n.TranslateResult; t(key: VueI18n.Path, locale: VueI18n.Locale, values?: VueI18n.Values): VueI18n.TranslateResult; tc(key: VueI18n.Path, choice?: VueI18n.Choice, values?: VueI18n.Values): string; tc( key: VueI18n.Path, choice: VueI18n.Choice, locale: VueI18n.Locale, values?: VueI18n.Values, ): string; te(key: VueI18n.Path, locale?: VueI18n.Locale): boolean; d( value: number | Date, key?: VueI18n.Path, locale?: VueI18n.Locale, ): VueI18n.DateTimeFormatResult; d(value: number | Date, args?: { [key: string]: string }): VueI18n.DateTimeFormatResult; d(value: number | Date, options?: VueI18n.DateTimeFormatOptions): VueI18n.DateTimeFormatResult; n(value: number, key?: VueI18n.Path, locale?: VueI18n.Locale): VueI18n.NumberFormatResult; n(value: number, args?: { [key: string]: string }): VueI18n.NumberFormatResult; n(value: number, options?: VueI18n.NumberFormatOptions, locale?: VueI18n.Locale): VueI18n.NumberFormatResult; getLocaleMessage(locale: VueI18n.Locale): VueI18n.LocaleMessageObject; setLocaleMessage(locale: VueI18n.Locale, message: VueI18n.LocaleMessageObject): void; mergeLocaleMessage(locale: VueI18n.Locale, message: VueI18n.LocaleMessageObject): void; getDateTimeFormat(locale: VueI18n.Locale): VueI18n.DateTimeFormat; setDateTimeFormat(locale: VueI18n.Locale, format: VueI18n.DateTimeFormat): void; mergeDateTimeFormat(locale: VueI18n.Locale, format: VueI18n.DateTimeFormat): void; getNumberFormat(locale: VueI18n.Locale): VueI18n.NumberFormat; setNumberFormat(locale: VueI18n.Locale, format: VueI18n.NumberFormat): void; mergeNumberFormat(locale: VueI18n.Locale, format: VueI18n.NumberFormat): void; getChoiceIndex: (choice: number, choicesLength: number) => number; } declare class VueI18n { constructor(options?: VueI18n.I18nOptions) readonly messages: VueI18n.LocaleMessages; readonly dateTimeFormats: VueI18n.DateTimeFormats; readonly numberFormats: VueI18n.NumberFormats; readonly availableLocales: VueI18n.Locale[]; locale: VueI18n.Locale; fallbackLocale: VueI18n.FallbackLocale; missing: VueI18n.MissingHandler; formatter: VueI18n.Formatter; formatFallbackMessages: boolean; silentTranslationWarn: boolean | RegExp; silentFallbackWarn: boolean | RegExp; preserveDirectiveContent: boolean; pluralizationRules: VueI18n.PluralizationRulesMap; warnHtmlInMessage: VueI18n.WarnHtmlInMessageLevel; postTranslation: VueI18n.PostTranslationHandler; sync: boolean; t(key: VueI18n.Path, values?: VueI18n.Values): VueI18n.TranslateResult; t(key: VueI18n.Path, locale: VueI18n.Locale, values?: VueI18n.Values): VueI18n.TranslateResult; tc(key: VueI18n.Path, choice?: VueI18n.Choice, values?: VueI18n.Values): string; tc(key: VueI18n.Path, choice: VueI18n.Choice, locale: VueI18n.Locale, values?: VueI18n.Values): string; te(key: VueI18n.Path, locale?: VueI18n.Locale): boolean; d(value: number | Date, key?: VueI18n.Path, locale?: VueI18n.Locale): VueI18n.DateTimeFormatResult; d(value: number | Date, args?: { [key: string]: string }): VueI18n.DateTimeFormatResult; d(value: number | Date, options?: VueI18n.DateTimeFormatOptions): VueI18n.DateTimeFormatResult; n(value: number, key?: VueI18n.Path, locale?: VueI18n.Locale): VueI18n.NumberFormatResult; n(value: number, args?: { [key: string]: string }): VueI18n.NumberFormatResult; n(value: number, options?: VueI18n.NumberFormatOptions, locale?: VueI18n.Locale): VueI18n.NumberFormatResult; getLocaleMessage(locale: VueI18n.Locale): VueI18n.LocaleMessageObject; setLocaleMessage(locale: VueI18n.Locale, message: VueI18n.LocaleMessageObject): void; mergeLocaleMessage(locale: VueI18n.Locale, message: VueI18n.LocaleMessageObject): void; getDateTimeFormat(locale: VueI18n.Locale): VueI18n.DateTimeFormat; setDateTimeFormat(locale: VueI18n.Locale, format: VueI18n.DateTimeFormat): void; mergeDateTimeFormat(locale: VueI18n.Locale, format: VueI18n.DateTimeFormat): void; getNumberFormat(locale: VueI18n.Locale): VueI18n.NumberFormat; setNumberFormat(locale: VueI18n.Locale, format: VueI18n.NumberFormat): void; mergeNumberFormat(locale: VueI18n.Locale, format: VueI18n.NumberFormat): void; /** * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)` * @param choicesLength {number} an overall amount of available choices * @returns a final choice index */ getChoiceIndex: (choice: number, choicesLength: number) => number; static install: PluginFunction; static version: string; static availabilities: VueI18n.IntlAvailability; } declare module 'vue/types/vue' { interface Vue { readonly $i18n: VueI18n & IVueI18n; $t: typeof VueI18n.prototype.t; $tc: typeof VueI18n.prototype.tc; $te: typeof VueI18n.prototype.te; $d: typeof VueI18n.prototype.d; $n: typeof VueI18n.prototype.n; } } declare module 'vue/types/options' { interface ComponentOptions { i18n?: { messages?: VueI18n.LocaleMessages; dateTimeFormats?: VueI18n.DateTimeFormats; numberFormats?: VueI18n.NumberFormats; sharedMessages?: VueI18n.LocaleMessages; }; } } export default VueI18n; ================================================ FILE: types/test/component.ts ================================================ import Vue from 'vue' import VueI18n from '../index' // setup locale info for root Vue instance const i18n = new VueI18n({ locale: 'ja', messages: { en: { message: { hello: 'hello world', greeting: 'good morning' } }, ja: { message: { hello: 'こんにちは、世界', greeting: 'おはようございます' } } } }) // Define component const Component1 = { template: `

Component1 locale messages: {{ $t("message.hello") }}

Fallback global locale messages: {{ $t("message.greeting") }}

`, i18n: { // `i18n` option messages: { en: { message: { hello: 'hello component1' } }, ja: { message: { hello: 'こんにちは、component1' } } }, sharedMessages: { en: { buttons: { save: 'Save' } }, ja: { buttons: { save: 'Save' } } } } } new Vue({ i18n, components: { Component1 } }).$mount('#app') ================================================ FILE: types/test/index.ts ================================================ import Vue from 'vue' import VueI18n, { DateTimeFormatOptions, NumberFormatOptions } from '../index' /* import * as VueI18n from 'vue-i18n'; import * as Vue from 'vue'; */ /** * VueI18n.install */ Vue.use(VueI18n) VueI18n.install(Vue) /** * VueI18n.version */ VueI18n.version // $ExpectType string /** * VueI18n.availabilities */ VueI18n.availabilities // $ExpectType IntlAvailability /** * VueI18n Instance */ const locale = 'locale' const key = 'key' const value = 'value' const dateTimeFormatOptions: DateTimeFormatOptions = { year: '2-digit', timeZone: 'Asia/Tokyo' } const numberFormatOptions: NumberFormatOptions = { style: 'currency', currency: 'JPY' } const i18n = new VueI18n({ locale, fallbackLocale: locale, messages: { [locale]: { [key]: value } }, dateTimeFormats: { [locale]: { [key]: dateTimeFormatOptions } }, numberFormats: { [locale]: { [key]: numberFormatOptions } }, formatter: { interpolate (message, values) { return [message] } }, modifiers: { foo: (str) => 'bar' }, missing (locale, key, vm) { }, fallbackRoot: false, sync: true, silentTranslationWarn: true, silentFallbackWarn: true, preserveDirectiveContent: true }) i18n.messages[locale][key] // $ExpectType LocaleMessage i18n.dateTimeFormats[locale][key] // $ExpectType DateTimeFormatOptions i18n.numberFormats[locale][key] // $ExpectType NumberFormatOptions i18n.locale // $ExpectType string i18n.fallbackLocale // $ExpectType string i18n.missing // $ExpectType MissingHandler i18n.formatter // $ExpectType Formatter i18n.silentTranslationWarn // $ExpectType boolean i18n.silentFallbackWarn // $ExpectType boolean i18n.preserveDirectiveContent // $ExpectType boolean i18n.setLocaleMessage // $ExpectType (locale: string, message: LocaleMessageObject) => void i18n.getLocaleMessage // $ExpectType (locale: string) => LocaleMessageObject i18n.mergeLocaleMessage // $ExpectType (locale: string, message: LocaleMessageObject) => void i18n.setDateTimeFormat // $ExpectType (locale: string, format: DateTimeFormat) => void i18n.getDateTimeFormat // $ExpectType (locale: string) => DateTimeFormat i18n.mergeDateTimeFormat // $ExpectType (locale: string, format: DateTimeFormat) => void i18n.setNumberFormat // $ExpectType (locale: string, format: NumberFormat) => void i18n.getNumberFormat // $ExpectType (locale: string) => NumberFormat i18n.mergeNumberFormat // $ExpectType (locale: string, format: NumberFormat) => void // $ExpectType { (key: string, values?: { [key: string]: any; } | undefined): TranslateResult; (key: string, locale: string, values?: { [key: string]: any; } | undefined): TranslateResult; } i18n.t // tslint:disable-next-line:max-line-length // $ExpectType { (key: string, choice?: number | undefined, values?: { [key: string]: any; } | undefined): string; (key: string, choice: number, locale: string, values?: { [key: string]: any; } | undefined): string; } i18n.tc // $ExpectType (key: string, locale?: string | undefined) => boolean i18n.te // tslint:disable-next-line:max-line-length // $ExpectType { (value: number | Date, key?: string | undefined, locale?: string | undefined): string; (value: number | Date, args?: { [key: string]: string; } | undefined): string; } i18n.d // tslint:disable-next-line:max-line-length // $ExpectType { (value: number, key?: string | undefined, locale?: string | undefined): string; (value: number, args?: { [key: string]: string; } | undefined): string; } i18n.n /** * Vue */ const vm = new Vue({ i18n }) vm.$i18n // $ExpectType VueI18n vm.$t(key) // $ExpectType TranslateResult vm.$t(key, ['', 0, false, null, undefined]) // $ExpectType TranslateResult vm.$t(key, { x: 'x' }) // $ExpectType TranslateResult vm.$t(key, locale) vm.$t(key, locale, ['', 0, false, null, undefined]) // $ExpectType TranslateResult vm.$t(key, locale, { x: 'x' }) // $ExpectType TranslateResult vm.$tc(key) // $ExpectType string vm.$tc(key, 1) // $ExpectType string vm.$tc(key, 1, []) // $ExpectType string vm.$tc(key, 1, {}) // $ExpectType string vm.$tc(key, 1, locale) // $ExpectType string vm.$tc(key, 1, locale, []) // $ExpectType string vm.$tc(key, 1, locale, {}) // $ExpectType string vm.$te(key) // $ExpectType boolean vm.$te(key, locale) // $ExpectType boolean vm.$d(1, key) // $ExpectType string vm.$d(1, key, locale) // $ExpectType string vm.$d(new Date(), { key, locale }) // $ExpectType string vm.$n(1, key) // $ExpectType string vm.$n(1, key, locale) // $ExpectType string vm.$n(100, { key, locale }) // $ExpectType string /** * VueI18n */ { let path: VueI18n.Path let locale: VueI18n.Locale let values: VueI18n.Values let choice: VueI18n.Choice let localeMessage: VueI18n.LocaleMessage let localeMessageObject: VueI18n.LocaleMessageObject let localeMessageArray: VueI18n.LocaleMessageArray let localeMessages: VueI18n.LocaleMessages let translateResult: VueI18n.TranslateResult let dateTimeFormatOptions: VueI18n.DateTimeFormatOptions let dateTimeFormat: VueI18n.DateTimeFormat let dateTimeFormats: VueI18n.DateTimeFormats let numberFormatOptions: VueI18n.NumberFormatOptions let NumberFormat: VueI18n.NumberFormat let numberFormats: VueI18n.NumberFormats let formatter: VueI18n.Formatter let missingHandler: VueI18n.MissingHandler let i18nOptions: VueI18n.I18nOptions } ================================================ FILE: types/tsconfig.json ================================================ { "compilerOptions": { "module": "esnext", "moduleResolution": "node", "allowSyntheticDefaultImports": true, "strict": true, "noEmit": true, "lib": ["esnext", "dom"] }, "include": [ "**/*.d.ts" ] } ================================================ FILE: vetur/attributes.json ================================================ { "i18n/path" : { "description": "[required]\nKeypath of the locale message", "type": "string" }, "i18n/locale" : { "description": "[optional]\nLocale to be used in this translation", "type": "string" }, "i18n/tag" : { "description": "[optional]\nWhich tag to render, default is \"span\"", "type": "string" }, "i18n/places": { "description": "[optional after v8.14]\nWill be removed in the next major version, use the slot syntax instead\n\nhttp://kazupon.github.io/vue-i18n/guide/interpolation.html#slots-syntax-usage", "type": "array|object" }, "i18n-n/value" : { "description": "[required]\nNumber to be used in formatting", "type": "number" }, "i18n-n/format": { "description": "[optional]\nNumber format name or object with explicit format options", "type": "string|object" }, "i18n-n/locale" : { "description": "[optional]\nLocale to be used in this translation", "type": "string" }, "i18n-n/tag" : { "description": "[optional]\nWhich tag to render, default is `span`", "type": "string" } } ================================================ FILE: vetur/tags.json ================================================ { "i18n": { "attributes": [ "path", "locale", "tag", "places" ], "description": "This is a functional component that can be used when HTML interpolation is needed.\n\nhttp://kazupon.github.io/vue-i18n/guide/interpolation.html#basic-usage" }, "i18n-n": { "attributes": [ "value", "format", "locale", "tag" ], "description": "This functional component provides a way to use HTML interpolation in pair with number formatting.\n\nhttp://kazupon.github.io/vue-i18n/guide/number.html#custom-formatting" } } ================================================ FILE: vuepress/.vuepress/components/sponsor-button.vue ================================================ ================================================ FILE: vuepress/.vuepress/config.js ================================================ const head = [] if (process.env.NODE_ENV === 'production') { head.push([ 'meta', { name: 'theme-color', content: '#3eaf7c' }, 'script', { src: 'https://unpkg.com/thesemetrics@latest', async: '' } ]) } module.exports = { base: '/vue-i18n/', head, locales: { '/': { lang: 'en-US', title: 'Vue I18n', description: 'Vue I18n is internationalization plugin for Vue.js' }, '/zh/': { lang: 'zh-CN', title: 'Vue I18n', description: 'Vue I18n 是 Vue.js 的国际化插件' }, '/ru/': { lang: 'ru-RU', title: 'Vue I18n', description: 'Vue I18n — плагин для интернационализации во Vue.js' }, '/pt/': { lang: 'pt-BR', title: 'Vue I18n', description: 'Vue I18n é um internacionalizador de idiomas para Vue.js' }, }, head: head, serviceWorker: false, themeConfig: { carbonAds: { carbon: 'CEAIC53M', placement: 'kazupongithubio' }, repo: 'kazupon/vue-i18n', editLinks: true, sidebarDepth: 3, docsDir: 'vuepress', locales: { '/': { label: 'English', selectText: 'Languages', editLinkText: 'Edit this page on GitHub', lastUpdated: 'Last Updated', nav: [ { text: 'Guide', link: '/guide/formatting' }, { text: 'API', link: '/api/' }, { text: 'Ecosystem', items: [ { text: 'Official Tooling', items: [ { text: 'Vue CLI Plugin', link: 'https://github.com/kazupon/vue-cli-plugin-i18n' }, { text: 'Webpack Loader', link: 'https://github.com/kazupon/vue-i18n-loader' }, { text: 'ESLint Plugin', link: 'https://intlify.github.io/eslint-plugin-vue-i18n/' }, { text: 'Extensions', link: 'https://github.com/kazupon/vue-i18n-extensions' } ] }, { text: '3rd Party Tooling', items: [ { text: 'inlang', link: 'https://marketplace.visualstudio.com/items?itemName=inlang.vs-code-extension' }, { text: 'i18n Ally', link: 'https://marketplace.visualstudio.com/items?itemName=lokalise.i18n-ally' }, { text: 'BabelEdit', link: 'https://www.codeandweb.com/babeledit?utm_campaign=vue-i18n-2019-01' } ] }, { text: '3rd Party Integrations', items: [ { text: 'Localazy', link: 'https://localazy.com/blog/how-to-localize-vuejs-app-with-vue-i18n-and-localazy?utm_source=kazupon&utm_medium=banner&utm_campaign=sponsorships_kazupon&utm_content=logo' }, { text: 'Crowdin', link: 'https://crowdin.com/teams/engineering?utm_source=vue-i18n.intlify.dev&utm_medium=referral' } ] } ] }, { text: 'Release Notes', link: 'https://github.com/kazupon/vue-i18n/releases' }, { text: 'v9.x', link: 'https://vue-i18n.intlify.dev' } ], sidebar: [ '/introduction', '/started', '/installation', { title: 'Guide', collapsable: false, children: [ '/guide/formatting', '/guide/pluralization', '/guide/datetime', '/guide/number', '/guide/messages', '/guide/fallback', '/guide/component', '/guide/directive', '/guide/interpolation', '/guide/sfc', '/guide/hot-reload', '/guide/locale', '/guide/lazy-loading', '/guide/tooling' ] } ] }, '/zh/': { label: '简体中文', selectText: '选择语言', editLinkText: '在 GitHub 上编辑此页', lastUpdated: '最近一次更新', nav: [ { text: '指南', link: '/zh/guide/formatting', }, { text: 'API', link: '/zh/api/' }, { text: '生态', items: [ { text: '官方工具', items: [ { text: '脚手架插件', link: 'https://github.com/kazupon/vue-cli-plugin-i18n' }, { text: 'Webpack Loader', link: 'https://github.com/kazupon/vue-i18n-loader' }, { text: 'ESLint 插件', link: 'https://intlify.github.io/eslint-plugin-vue-i18n/' }, { text: '扩展', link: 'https://github.com/kazupon/vue-i18n-extensions' } ] }, { text: '翻译工具', items: [ { text: 'BabelEdit', link: 'https://www.codeandweb.com/babeledit?utm_campaign=vue-i18n-2019-01' } ] } ] }, { text: '赞助', link: 'https://www.patreon.com/kazupon' }, { text: '发布日志', link: 'https://github.com/kazupon/vue-i18n/releases' }, { text: 'v9', link: 'https://vue-i18n.intlify.dev' } ], sidebar: [ '/zh/introduction', '/zh/started', '/zh/installation', { title: 'Guide', collapsable: false, children: [ '/zh/guide/formatting', '/zh/guide/pluralization', '/zh/guide/datetime', '/zh/guide/number', '/zh/guide/messages', '/zh/guide/fallback', '/zh/guide/component', '/zh/guide/directive', '/zh/guide/interpolation', '/zh/guide/sfc', '/zh/guide/hot-reload', '/zh/guide/locale', '/zh/guide/lazy-loading', '/zh/guide/tooling' ] } ] }, '/ru/': { label: 'Русский', selectText: 'Переводы', editLinkText: 'Изменить эту страницу на GitHub', lastUpdated: 'Последнее обновление', nav: [ { text: 'Руководство', link: '/ru/guide/formatting', }, { text: 'Справочник API', link: '/ru/api/' }, { text: 'Экосистема', items: [ { text: 'Оф. инструментарий', items: [ { text: 'Плагин для Vue CLI', link: 'https://github.com/kazupon/vue-cli-plugin-i18n' }, { text: 'Загрузчик Webpack', link: 'https://github.com/kazupon/vue-i18n-loader' }, { text: 'Плагин для ESLint', link: 'https://intlify.github.io/eslint-plugin-vue-i18n/' }, { text: 'Расширения', link: 'https://github.com/kazupon/vue-i18n-extensions' } ] }, { text: 'Сторонние разработки', items: [ { text: 'BabelEdit', link: 'https://www.codeandweb.com/babeledit?utm_campaign=vue-i18n-2019-01' }, { text: 'i18n Ally', link: 'https://marketplace.visualstudio.com/items?itemName=antfu.i18n-ally' } ] } ] }, { text: 'Patreon', link: 'https://www.patreon.com/kazupon' }, { text: 'История изменений', link: 'https://github.com/kazupon/vue-i18n/releases' }, { text: 'v9', link: 'https://vue-i18n.intlify.dev' } ], sidebar: [ '/ru/introduction', '/ru/started', '/ru/installation', { title: 'Руководство', collapsable: false, children: [ '/ru/guide/formatting', '/ru/guide/pluralization', '/ru/guide/datetime', '/ru/guide/number', '/ru/guide/messages', '/ru/guide/fallback', '/ru/guide/component', '/ru/guide/directive', '/ru/guide/interpolation', '/ru/guide/sfc', '/ru/guide/hot-reload', '/ru/guide/locale', '/ru/guide/lazy-loading', '/ru/guide/tooling' ] } ] }, '/pt/': { label: 'Português', selectText: 'Idiomas', editLinkText: 'Editar esta página no GitHub', lastUpdated: 'Última atualização', nav: [ { text: 'Guia', link: '/pt/guide/formatting' }, { text: 'API', link: '/pt/api/' }, { text: 'Ecossistema', items: [ { text: 'Ferramentas Oficiais', items: [ { text: 'Vue CLI Plugin', link: 'https://github.com/kazupon/vue-cli-plugin-i18n' }, { text: 'Webpack Loader', link: 'https://github.com/kazupon/vue-i18n-loader' }, { text: 'ESLint Plugin', link: 'https://intlify.github.io/eslint-plugin-vue-i18n/' }, { text: 'Extensoes', link: 'https://github.com/kazupon/vue-i18n-extensions' } ] }, { text: 'Ferramentas de terceiros', items: [ { text: 'BabelEdit', link: 'https://www.codeandweb.com/babeledit?utm_campaign=vue-i18n-2019-01' }, { text: 'i18n Ally', link: 'https://marketplace.visualstudio.com/items?itemName=antfu.i18n-ally' } ] } ] }, { text: 'Patreon', link: 'https://www.patreon.com/kazupon' }, { text: 'Notas de Lançamento', link: 'https://github.com/kazupon/vue-i18n/releases' }, { text: 'v9', link: 'https://vue-i18n.intlify.dev' } ], sidebar: [ '/pt/introduction', '/pt/started', '/pt/installation', { title: 'Guia', collapsable: false, children: [ '/pt/guide/formatting', '/pt/guide/pluralization', '/pt/guide/datetime', '/pt/guide/number', '/pt/guide/messages', '/pt/guide/fallback', '/pt/guide/component', '/pt/guide/directive', '/pt/guide/interpolation', '/pt/guide/sfc', '/pt/guide/hot-reload', '/pt/guide/locale', '/pt/guide/lazy-loading', '/pt/guide/tooling' ] } ] }, } } } ================================================ FILE: vuepress/.vuepress/theme/Layout.vue ================================================ ================================================ FILE: vuepress/.vuepress/theme/components/CarbonAds.vue ================================================ ================================================ FILE: vuepress/.vuepress/theme/index.js ================================================ module.exports = { extend: '@vuepress/theme-default' } ================================================ FILE: vuepress/README.md ================================================ --- home: true heroImage: ./vue-i18n-logo.png actionText: Get Started → actionLink: introduction.md footer: MIT Licensed | Copyright © 2020 kazuya kawaguchi --- Vue I18n v8 has reached EOL and is no longer actively maintained. Please upgrade to Vue I18n v9 or later. **The seucirty hotfix is only provided for Vue I18n v8.28 until end of 2024**. Thereafter, security fix is not provide for that version later. However, if it is absolutely necessary, please contact to contact@frapwings.jp

Easy

You can introduce internationalization into your app with simple API

Powerful

In addition to simple translation, support localization such as pluralization, number, datetime ... etc

Component-oriented

You can manage locale messages on single file component

🏅 Platinum Sponsors

zenarchitects

✨ Special Sponsors

PLAID

🥇 Gold Sponsors

NuxtLabs Localazy Crowdin

🥈 Silver Sponsors

🥉 Bronze Sponsors

vuemastery vuemastery
================================================ FILE: vuepress/api/README.md ================================================ --- sidebar: auto --- # API references ## Extension of Vue ### Vue constructor options #### i18n * **Type:** `I18nOptions` Component based localization option. * **See also:** `VueI18n` class constructor options ### Vue injected methods #### $t * **Arguments:** * `{Path} key`: required * `{Locale} locale`: optional * `{Array | Object} values`: optional * **Return:** `TranslateResult` Localize the locale message of `key`. Localize in preferentially component locale messages than global locale messages. If not specified component locale messages, localize with global locale messages. If you specified `locale`, localize the locale messages of `locale`. If you specified `key` of list / named formatting local messages, you must specify `values` too. For `values` more details see [Formatting](../guide/formatting.md). If default pluralization does not suit your needs, see [pluralization rules in constructor options](#pluralizationrules) and [custom pluralization](../guide/pluralization.md). :::danger Tip Note that you need to guarantee this context equal to component instance in lifecycle methods (e.g. in `data` options, `const $t = this.$t.bind(this)`). ::: #### $tc * **Arguments:** * `{Path} key`: required * `{number} choice`: optional, default 1 * `{Locale} locale`: optional * `{string | Array | Object} values`: optional * **Return:** `TranslateResult` Localize the locale message of `key` with pluralization. Localize in preferentially component locale messages than global locale messages. If not specified component locale messages, localize with global locale messages. If you specified `locale`, localize the locale messages of `locale`. If you will specify string value to `values`, localize the locale messages of value. If you will specify Array or Object value to `values`, you must specify with `values` of $t. :::danger Tip Note that you need to guarantee this context equal to component instance in lifecycle methods (e.g. in `data` options, `const $tc = this.$tc.bind(this)`). ::: #### $te * **Arguments:** * `{Path} key`: required * `{Locale} locale`: optional * **Return:** `boolean` Check whether key exists. In Vue instance, If not specified component locale messages, check with global locale messages. If you specified `locale`, check the locale messages of `locale`. :::danger Tip Note that you need to guarantee this context equal to component instance in lifecycle methods (e.g. in `data` options, `const $te = this.$te.bind(this)`). ::: #### $d > :new: 7.0+ * **Arguments:** * `{number | Date} value`: required * `{Path | Object} key`: optional * `{Locale | Object} locale`: optional * **Return:** `DateTimeFormatResult` Localize the datetime of `value` with datetime format of `key`. The datetime format of `key` need to register to `dateTimeFormats` option of `VueI18n` class, and depend on `locale` option of `VueI18n` constructor. If you will specify `locale` argument, it will have priority over `locale` option of `VueI18n` constructor. If the datetime format of `key` not exist in `dateTimeFormats` option, fallback to depend on `fallbackLocale` option of `VueI18n` constructor. :::danger Tip Note that you need to guarantee this context equal to component instance in lifecycle methods (e.g. in `data` options, `const $d = this.$d.bind(this)`). ::: #### $n > :new: 7.0+ * **Arguments:** * `{number} value`: required * `{Path | Object} format`: optional * `{Locale} locale`: optional * **Return:** `NumberFormatResult` Localize the number of `value` with number format of `format`. The number format of `format` need to register to `numberFormats` option of `VueI18n` class, and depend on `locale` option of `VueI18n` constructor. If you will specify `locale` argument, it will have priority over `locale` option of `VueI18n` constructor. If the number format of `format` not exist in `numberFormats` option, fallback to depend on `fallbackLocale` option of `VueI18n` constructor. If the second `format` argument specified as an object, it should have the following properties: * `key {Path}`: optional, number format * `locale {Locale}`: optional, locale * `compactDisplay {string}`: optional, number format option * `currency {string}`: optional, number format option * `currencyDisplay {string}`: optional, number format option * `currencySign {string}`: optional, number format option * `localeMatcher {string}`: optional, number format option * `notation {string}`: optional, number format option * `numberingSystem {string}`: optional, number format option * `signDisplay {string}`: optional, number format option * `style {string}`: optional, number format option * `unit {string}`: optional, number format option * `unitDisplay {string}`: optional, number format option * `useGrouping {boolean}`: optional, number format option * `minimumIntegerDigits {string}`: optional, number format option * `minimumFractionDigits {string}`: optional, number format option * `maximumFractionDigits {string}`: optional, number format option * `minimumSignificantDigits {string}`: optional, number format option * `maximumSignificantDigits {string}`: optional, number format option Any specified number format options will have priority over `numberFormats` of `VueI18n` constructor. :::danger Tip Note that you need to guarantee this context equal to component instance in lifecycle methods (e.g. in `data` options, `const $n = this.$n.bind(this)`). ::: ### Injected properties #### $i18n * **Type:** `I18n` * **Read only** Get a `VueI18n` instance. If you are specify. If you have specified an `i18n` option at component options, you will be able to get a `VueI18n` instance at the component, Otherwise, you will be able get root `VueI18n` instance. ## `VueI18n` class `VueI18n` class implement `I18n` interface of [flowtype definitions](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js) ### Static properties #### version * **Type:** `string` vue-i18n version. #### availabilities > :new: 7.0+ * **Type:** `IntlAvailability` Whether the following internationalization features are available: * `{boolean} dateTimeFormat`: locale sensitive datetime formatting * `{boolean} numberFormat`: locale sensitive number formatting The above internationalization features are depends on [the browser environments](http://kangax.github.io/compat-table/esintl/), due to implement with ECMAScript Internationalization API (ECMA-402). ### Constructor options You can specify the below some options of `I18nOptions` constructor options of [flowtype definitions](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js) #### locale * **Type:** `Locale` * **Default:** `'en-US'` The locale of localization. If the locale contains a territory and a dialect, this locale contains an implicit fallback. #### fallbackLocale * **Type:** `FallbackLocale` * **Default:** `false` The locale of fallback localization. For more complex fallback definitions see [fallback](../guide/fallback.md). #### messages * **Type:** `LocaleMessages` * **Default:** `{}` The locale messages of localization. #### dateTimeFormats > :new: 7.0+ * **Type:** `DateTimeFormats` * **Default:** `{}` The datetime formats of localization. * **See also:** `DateTimeFormats` type of [flowtype definitions](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js) #### numberFormats > :new: 7.0+ * **Type:** `NumberFormats` * **Default:** `{}` The number formats of localization. * **See also:** `NumberFormats` type of [flowtype definitions](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js) #### formatter * **Type:** `Formatter` * **Default:** Built in formatter The formatter that implemented with `Formatter` interface. #### modifiers > :new: 8.15.0+ * **Type:** `Modifiers` * **Default:** `lower` and `upper` modifiers Modifiers functions for linked messages #### missing * **Type:** `MissingHandler` * **Default:** `null` A handler for localization missing. The handler gets called with the localization target locale, localization path key, the Vue instance and values. If missing handler is assigned, and occurred localization missing, it's not warned. #### fallbackRoot * **Type:** `Boolean` * **Default:** `true` In the component localization, whether to fall back to root level (global) localization when localization fails. If `false`, it's warned, and is returned the key. #### fallbackRootWithEmptyString > :new: 8.26+ - **Type:** `Boolean` - **Default:** `true` In the component localization, whether to fall back to root level (global) localization when local message is an empty string. Please note the default behavior in vue-i18n 9.x is to not falling back to root for local message that is empty string. If `false`, the empty local message will not fall back to root and will be kept as empty string. #### sync * **Type:** `Boolean` * **Default:** `true` Whether synchronize the root level locale to the component localization locale. If `false`, regardless of the root level locale, localize for each component locale. #### silentTranslationWarn > 6.1+, :up: 8.13 * **Type:** `Boolean | RegExp` * **Default:** `false` Whether suppress warnings outputted when localization fails. If `true`, suppress localization fail warnings. If you use regular expression, you can suppress localization fail warnings that it match with translation `key` (e.g. `$t`). #### silentFallbackWarn > :new: 8.8+, :up: 8.13 * **Type:** `Boolean | RegExp` * **Default:** `false` Whether suppress warnings when falling back to either `fallbackLocale` or `root`. If `true`, warnings will be generated only when no translation is available at all, and not for fallbacks. If you use regular expression, you can suppress the fallback warnings that it match `key` (e.g. `$t`). #### pluralizationRules > 8.5+ * **Type:** `PluralizationRules` * **Default:** `{}` A set of rules for word pluralization in a following format: ```js { // Key - locale for the rule to be applied to. // Value - mapping function that maps a choice index from `$tc` to the actual choice of the plural word. (See getChoiceIndex for details) 'pt': function(choice, choiceIndex) => Number/* index of the plural word */; 'ru': function(choice, choiceIndex) => Number/* index of the plural word */; 'en': function(choice, choiceIndex) => Number/* index of the plural word */; 'jp': function(choice, choiceIndex) => Number/* index of the plural word */; } ``` #### preserveDirectiveContent > 8.7+ * **Type:** `Boolean` * **Default:** `false` Whether `v-t` directive's element should preserve `textContent` after directive is unbinded. #### warnHtmlInMessage > 8.11+ * **Type:** `WarnHtmlInMessageLevel` * **Default:** `off` Whether to allow the use locale messages of HTML formatting. See the `warnHtmlInMessage` property. :::danger Important!! In next major version, `warnHtmlInMessage` option is `warn` as default. ::: #### sharedMessages > 8.12+ * **Type:** `LocaleMessages` * **Default:** `undefined` The shared locale messages of localization for components. More detail see [Component based localization](../guide/component.md#shared-locale-messages-for-components). #### postTranslation > 8.16+ * **Type:** `PostTranslationHandler` * **Default:** `null` A handler for post processing of translation. The handler gets after being called with the `$t`, `t`, `$tc`, and `tc`. This handler is useful if you want to filter on translated text such as space trimming. #### componentInstanceCreatedListener > 8.18+ * **Type:** `ComponentInstanceCreatedListener` * **Default:** `null` A handler for getting notified when component-local instance was created. The handler gets called with new and old (root) VueI18n instances. This handler is useful when extending the root VueI18n instance and wanting to also apply those extensions to component-local instance. #### escapeParameterHtml > 8.22+ * **Type:** `Boolean` * **Default:** `false` If `escapeParameterHtml` is configured as true then interpolation parameters are escaped before the message is translated. This is useful when translation output is used in `v-html` and the translation resource contains html markup (e.g. `` around a user provided value). This usage pattern mostly occurs when passing precomputed text strings into UI compontents. The escape process involves replacing the following symbols with their respective HTML character entities: `<`, `>`, `"`, `'`. Setting `escapeParameterHtml` as true should not break existing functionality but provides a safeguard against a subtle type of XSS attack vectors. ### Properties #### locale * **Type:** `Locale` * **Read/Write** The locale of localization. If the locale contains a territory and a dialect, this locale contains an implicit fallback. #### fallbackLocale * **Type:** `FallbackLocale` * **Read/Write** The locale of fallback localization. For more complex fallback definitions see [fallback](../guide/fallback.md). #### messages * **Type:** `LocaleMessages` * **Read only** The locale messages of localization. #### dateTimeFormats > :new: 7.0+ * **Type:** `DateTimeFormats` * **Read only** The datetime formats of localization. #### numberFormats > :new: 7.0+ * **Type:** `NumberFormats` * **Read only** The number formats of localization. #### availableLocales > :new: 8.9.0+ * **Type:** `Locale[]` * **Read only** The list of available locales in `messages` in lexical order. #### missing * **Type:** `MissingHandler` * **Read/Write** A handler for localization missing. #### formatter * **Type:** `Formatter` * **Read/Write** The formatter that implemented with `Formatter` interface. #### silentTranslationWarn > 6.1+, :up: 8.13 * **Type:** `Boolean | RegExp` * **Read/Write** Whether suppress warnings outputted when localization fails. #### silentFallbackWarn > :new: 8.8+, :up: 8.13 * **Type:** `Boolean | RegExp` * **Read/Write** Whether suppress fallback warnings when localization fails. #### pluralizationRules > 8.5+ * **Type:** `PluralizationRules` * **Read/Write** A set of locale-dependent rules for word pluralization. #### preserveDirectiveContent > 8.7+ * **Type:** `Boolean` * **Read/Write** Whether `v-t` directive's element should preserve `textContent` after directive is unbinded. #### warnHtmlInMessage > 8.11+ * **Type:** `WarnHtmlInMessageLevel` * **Read/Write** Whether to allow the use locale messages of HTML formatting. If you set `warn` or` error`, will check the locale messages on the VueI18n instance. If you are specified `warn`, a warning will be output at console. If you are specified `error` will occurred an Error. In VueI18n instance, set the `off` as default. #### postTranslation > 8.16+ * **Type:** `PostTranslationHandler` * **Read/Write** A handler for post processing of translation. ### Methods #### getChoiceIndex * **Arguments:** * `{number} choice` * `{number} choicesLength` * **Return:** `finalChoice {number}` Get pluralization index for current pluralizing number and a given amount of choices. Can be overridden through prototype mutation: ```js VueI18n.prototype.getChoiceIndex = /* custom implementation */ ``` However, for most usages [pluralizationRules constructor option](#pluralizationrules) should be enough. #### getLocaleMessage( locale ) * **Arguments:** * `{Locale} locale` * **Return:** `LocaleMessageObject` Get the locale message of locale. #### setLocaleMessage( locale, message ) * **Arguments:** * `{Locale} locale` * `{LocaleMessageObject} message` Set the locale message of locale. :::tip NOTE > 8.11+ If you set `warn` or` error` in the `warnHtmlInMessage` property, when this method is executed, it will check if HTML formatting is used for locale message. ::: #### mergeLocaleMessage( locale, message ) > 6.1+ * **Arguments:** * `{Locale} locale` * `{LocaleMessageObject} message` Merge the registered locale messages with the locale message of locale. :::tip NOTE > 8.11+ If you set `warn` or` error` in the `warnHtmlInMessage` property, when this method is executed, it will check if HTML formatting is used for locale message. ::: #### t( key, [locale], [values] ) * **Arguments:** * `{Path} key`: required * `{Locale} locale`: optional * `{Array | Object} values`: optional * **Return:** : `TranslateResult` This is the same as the `Function` returned with `$t` method. More detail see [$t](#t). #### tc( key, [choice], [values] ) * **Arguments:** * `{Path} key`: required * `{number} choice`: optional, default `1` * `{string | Array | Object} values`: optional * **Return:** `TranslateResult` This is the same as the `Function` returned `$tc` method. More detail see [$tc](#tc). #### te( key, [locale] ) * **Arguments:** * `{string} key`: required * `{Locale} locale`: optional * **Return:** `boolean` Check whether key path exists in global locale message. If you specified `locale`, check the locale message of `locale`. #### getDateTimeFormat ( locale ) > :new: 7.0+ * **Arguments:** * `{Locale} locale` * **Return:** `DateTimeFormat` Get the datetime format of locale. #### setDateTimeFormat ( locale, format ) > :new: 7.0+ * **Arguments:** * `{Locale} locale` * `{DateTimeFormat} format` Set the datetime format of locale. #### mergeDateTimeFormat ( locale, format ) > :new: 7.0+ * **Arguments:** * `{Locale} locale` * `{DateTimeFormat} format` Merge the registered datetime formats with the datetime format of locale. #### d( value, [key], [locale] ) > :new: 7.0+ * **Arguments:** * `{number | Date} value`: required * `{Path | Object} key`: optional * `{Locale | Object} locale`: optional * **Return:** `DateTimeFormatResult` This is the same as `$d` method of Vue instance method. More detail see [$d](#d). #### getNumberFormat ( locale ) > :new: 7.0+ * **Arguments:** * `{Locale} locale` * **Return:** `NumberFormat` Get the number format of locale. #### setNumberFormat ( locale, format ) > :new: 7.0+ * **Arguments:** * `{Locale} locale` * `{NumberFormat} format` Set the number format of locale. #### mergeNumberFormat ( locale, format ) > :new: 7.0+ * **Arguments:** * `{Locale} locale` * `{NumberFormat} format` Merge the registered number formats with the number format of locale. #### n( value, [format], [locale] ) > :new: 7.0+ * **Arguments:** * `{number} value`: required * `{Path | Object} format`: optional * `{Locale} locale`: optional * **Return:** `NumberFormatResult` This is the same as `$n` method of Vue instance method. More detail see [$n](#n). ## Directives > :new: 7.3+ ### v-t * **Expects:** `string | Object` * **Modifiers:** * `.preserve`: (8.7.0+) preserves element `textContent` when directive is unbinded. * **Details:** Update the element `textContent` that localized with locale messages. You can use string syntax or object syntax. string syntax can be specified as a keypath of locale messages. If you can be used object syntax, you need to specify as the object key the following params: * `path`: required, key of locale messages * `locale`: optional, locale * `args`: optional, for list or named formatting :::tip NOTE The element `textContent` will be cleared by default when `v-t` directive is unbinded. This might be undesirable situation when used inside [transitions](https://vuejs.org/v2/guide/transitions.html). To preserve `textContent` data after directive unbind use `.preserve` modifier or global [`preserveDirectiveContent` option](#preservedirectivecontent). ::: * **Examples:** ```html

``` * **See also:** [Custom directive localization](../guide/directive.md) ## Components ### i18n functional component > :new: 7.0+ #### Props: * `path {Path}`: required, keypath of locale messages * `locale {Locale}`: optional, locale * `tag {string | boolean | Object}`: optional, default `'span'` * `places {Array | Object}`: optional (7.2+) :::danger Important!! In next major version, `places` prop is deprecated. Please switch to slots syntax. ::: #### Usage: ```html ``` ```js const messages = { en: { tos: 'Term of Service', term: 'I accept xxx {0}.' }, ja: { tos: '利用規約', term: '私は xxx の{0}に同意します。' } } const i18n = new VueI18n({ locale: 'en', messages }) new Vue({ i18n, data: { url: '/term' } }).$mount('#app') ``` #### See also: [Component interpolation](../guide/interpolation.md) ### i18n-n functional component > :new: 8.10+ #### Props: * `value {number}`: required, number to format * `format {string | NumberFormatOptions}`: optional, number format name or object with explicit format options * `locale {Locale}`: optional, locale * `tag {string | boolean | Object}`: optional, default `'span'` #### Usage: ```html
{{ slotProps.currency }}
``` ```js var numberFormats = { 'en-US': { currency: { style: 'currency', currency: 'USD' } }, 'ja-JP': { currency: { style: 'currency', currency: 'JPY' } } } const i18n = new VueI18n({ locale: 'en-US', numberFormats }) new Vue({ i18n, data: { money: 10234, } }).$mount('#app') ``` #### Scoped slots `` functional component can accept a number of named scoped slots. List of supported slot names is based on [`Intl.NumberFormat.formatToParts()` output types](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat/formatToParts): * `currency` * `decimal` * `fraction` * `group` * `infinity` * `integer` * `literal` * `minusSign` * `nan` * `plusSign` * `percentSign` Each of these named scoped slots will accept three scope parameters: * `[slotName] {FormattedNumberPartType}`: parameter of the same name as actual slot name (like `integer`) * `index {Number}`: index of the specific part in the array of number parts * `parts {Array}`: array of all formatted number parts #### See also: [Number custom formatting](../guide/number.md#custom-formatting) ## Special Attributes ### place > :new: 7.2+ #### Expects: `{number | string}` Used on component interpolation to indicate which index of list formatting or key of named formatting. For detailed usage, see the guide section linked below. #### See also: [Component interpolation](../guide/interpolation.md) ================================================ FILE: vuepress/guide/component.md ================================================ # Component based localization In general, locale info (e.g. `locale`,`messages`, etc) is set as constructor option of `VueI18n` instance and it sets `i18n` option as root Vue instance. Therefore you can globally translate using `$t` or `$tc` in the root Vue instance and any composed component. You can also manage locale info for each component separately, which might be more convenient due to Vue components oriented design. Component based localization example: ```js // setup locale info for root Vue instance const i18n = new VueI18n({ locale: 'ja', messages: { en: { message: { hello: 'hello world', greeting: 'good morning' } }, ja: { message: { hello: 'こんにちは、世界', greeting: 'おはようございます' } } } }) // Define component const Component1 = { template: `

Component1 locale messages: {{ $t("message.hello") }}

Fallback global locale messages: {{ $t("message.greeting") }}

`, i18n: { // `i18n` option, setup locale info for component messages: { en: { message: { hello: 'hello component1' } }, ja: { message: { hello: 'こんにちは、component1' } } } } } new Vue({ i18n, components: { Component1 } }).$mount('#app') ``` Template: ```html

{{ $t("message.hello") }}

``` Outputs the following: ```html

こんにちは、世界

Component1 locale messages: こんにちは、component1

Fallback global locale messages: おはようございます

``` As in the example above, if the component doesn't have the locale message, it falls back to globally defined localization info. The component uses the language set in the root instance (in the above example: `locale: 'ja'`). Note that, by default, falling back to root locale generates two warnings in the console: ``` [vue-i18n] Value of key 'message.greeting' is not a string! [vue-i18n] Fall back to translate the keypath 'message.greeting' with root locale. ``` To suppress these warnings (while keeping those which warn of the total absence of translation for the given key) set `silentFallbackWarn: true` when initializing the `VueI18n` instance. If you want to localize using the component locale, you can do that with `sync: false` and `locale` in the `i18n` option. ## Shared locale messages for components Sometimes you may want to import shared locale messages for certain components, not fallback from global locale messages (e.g. common messages of certain feature for components. You can use `sharedMessages` options of `i18n`. Common Locale Messages example: ```js export default { en: { buttons: { save: "Save", // ... } }, ja: { buttons: { save: "保存", // ... } } } ``` Components: ```js import commonMessage from './locales/common' // import common locale messages export default { name: 'ServiceModal', template: ` `, i18n: { messages: { ... }, sharedMessages: commonMessages } } ``` If `sharedMessages` option is specified along with the `messages` option, those messages will be merged into locale messages into the VueI18n instance of the target component. ## Translation in functional component When using a functional component, all data (including `props`, `children`, `slots`, `parent`, etc.) is passed through the `context` containing the attributes, and it doesn't recognize the `this` scope, so when using the vue-i18n on functional components, you must refer to `$t` as `parent.$t`, check the example below: ```html ... ... ``` ================================================ FILE: vuepress/guide/datetime.md ================================================ # DateTime localization :::tip Support Version :new: 7.0+ ::: You can localize the datetime with your definition formats. DateTime formats the below: ```js const dateTimeFormats = { 'en-US': { short: { year: 'numeric', month: 'short', day: 'numeric' }, long: { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long', hour: 'numeric', minute: 'numeric' } }, 'ja-JP': { short: { year: 'numeric', month: 'short', day: 'numeric' }, long: { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long', hour: 'numeric', minute: 'numeric', hour12: true } } } ``` As seen above, you can define named datetime format (e.g. `short`, `long`, etc), and you need to use [the options with ECMA-402 Intl.DateTimeFormat](http://www.ecma-international.org/ecma-402/2.0/#sec-intl-datetimeformat-constructor) After that, when using the locale messages, you need to specify the `dateTimeFormats` option of the `VueI18n` constructor: ```js const i18n = new VueI18n({ dateTimeFormats }) new Vue({ i18n }).$mount('#app') ``` Template the below: ```html

{{ $d(new Date(), 'short') }}

{{ $d(new Date(), 'long', 'ja-JP') }}

``` Output the below: ```html

Jan 18, 2021

2021年1月18日日曜日 午前5:47

``` ================================================ FILE: vuepress/guide/directive.md ================================================ # Custom directive localization :::tip Support Version :new: 7.3+ ::: You can translate not only with `v-t` custom directive, but also with the `$t` method. ## String syntax You can pass the keypath of locale messages with string syntax. Javascript: ```js new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { hello: 'hi there!' }, ja: { hello: 'こんにちは!' } } }), data: { path: 'hello' } }).$mount('#string-syntax') ``` Templates: ```html

``` Outputs: ```html

hi there!

hi there!

``` ## Object syntax You can use object syntax. Javascript: ```js new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { hello: 'hi {name}!' }, ja: { hello: 'こんにちは、{name}!' } } }), computed: { nickName () { return 'kazupon' } }, data: { path: 'hello' } }).$mount('#object-syntax') ``` Templates: ```html

``` Outputs: ```html

こんにちは、kazupon!

hi kazupon!

``` ## Use with transitions :::tip Support Version :new: 8.7+ ::: When `v-t` directive is applied to an element inside [`` component](https://vuejs.org/v2/api/#transition), you may notice that the translated message disappears during the transition. This behavior is related to the nature of the `` component implementation – all directives in the disappearing element inside the `` component will be destroyed **before the transition starts**. This behavior may result in content flickering on short animations, but is most noticeable on long transitions. To make sure directive content stays un-touched during a transition, just add the [`.preserve` modifier](../api/#v-t) to the `v-t` directive definition. Javascript: ```js new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { preserve: 'with preserve' }, } }), data: { toggle: true } }).$mount('#in-transitions') ``` Templates: ```html
``` It is also possible to set global settings on the `VueI18n` instance itself, which will affect all `v-t` directives without modifier. Javascript: ```js new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { preserve: 'with preserve' }, }, preserveDirectiveContent: true }), data: { toggle: true } }).$mount('#in-transitions') ``` Templates: ```html
``` About the above examples, see the [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/directive) ## `$t` vs `v-t` ### `$t` `$t` is an extended Vue instance method. It has the following pros and cons: #### Pros You can **flexibly** use mustache syntax `{{}}` in templates and also computed props and methods in Vue instance. #### Cons `$t` is executed **every time** when re-render occurs, so it does have translation costs. ### `v-t` `v-t` is a custom directive. It has the following pros and cons: #### Pros `v-t` has **better performance** than the `$t` method due to its cache with the custom directive, when translated once. Also, pre-translation is possible with the Vue compiler module which was provided by [`vue-i18n-extensions`](https://github.com/kazupon/vue-i18n-extensions). Therefore it's possible to make **more performance optimizations**. #### Cons `v-t` cannot be flexibly used like `$t`, it's rather **complex**. The translated content with `v-t` is inserted into the `textContent` of the element. Also, when you use server-side rendering, you need to set the [custom directive](https://github.com/kazupon/vue-i18n-extensions#directive-v-t-custom-directive-for-server-side) to `directives` option of the `createRenderer` function. ================================================ FILE: vuepress/guide/fallback.md ================================================ # Fallback localization *Summary: Use `fallbackLocale: ''` to choose which language to use when your preferred language lacks a translation.* ## Implicit fallback using locales If a `locale` is given containing a territory and an optional dialect, the implicit fallback is activated automatically. For example `de-DE-bavarian` would fallback 1. `de-DE-bavarian` 1. `de-DE` 1. `de` To suppress the automatic fallback, add the postfix exclamation mark `!`, for example `de-DE!` ## Explicit fallback with one locale Sometimes some items will not be translated into some languages. In this example, the item `hello` is available in English but not Japanese: ```js const messages = { en: { hello: 'Hello, world!' }, ja: { // Localization without translation to `hello` } } ``` If you want to use (say) `en` items when an item is not available in your desired locale, set the `fallbackLocale` option in the VueI18n constructor: ```js const i18n = new VueI18n({ locale: 'ja', fallbackLocale: 'en', messages }) ``` Template: ```html

{{ $t('hello') }}

``` Output: ```html

Hello, world!

``` By default, falling back to `fallbackLocale` generates two console warnings: ``` [vue-i18n] Value of key 'hello' is not a string! [vue-i18n] Fall back to translate the keypath 'hello' with 'en' locale. ``` To suppress these warnings (while keeping those which warn of the total absence of translation for the given key) set `silentFallbackWarn: true` when initializing the `VueI18n` instance. ## Explicit fallback with an array of locales It is possible to set more than one fallback locale by using an array of locales. For example ```javascript fallbackLocale: [ 'fr', 'en' ], ``` ## Explicit fallback with decision maps If more complex decision maps for fallback locales are required, it is possible to define decision maps with according fallback locales. Using the following decision map ```javascript fallbackLocale: { /* 1 */ 'de-CH': ['fr', 'it'], /* 2 */ 'zh-Hant': ['zh-Hans'], /* 3 */ 'es-CL': ['es-AR'], /* 4 */ 'es': ['en-GB'], /* 5 */ 'pt': ['es-AR'], /* 6 */ 'default': ['en', 'da'] }, ``` will result in the following fallback chains | locale | fallback chains | |--------|-----------------| | `'de-CH'` | de-CH > fr > it > en > da | | `'de'` | de > en > da | | `'zh-Hant'` | zh-Hant > zh-Hans > zh > en > da | | `'es-SP'` | es-SP > es > en-GB > en > da | | `'es-SP!'` | es-SP > en > da | | `'fr'` | fr > en > da | | `'pt-BR'` | pt-BR > pt > es-AR > es > en-GB > en > da | | `'es-CL'` | es-CL > es-AR > es > en-GB > en > da | ## Fallback interpolation *Summary: Set `formatFallbackMessages: true` to do template interpolation on translation keys when your language lacks a translation for a key.* Since the keys to the translations are strings, you can use a user-readable message (for a particular language) as a key. E.g. ```javascript const messages = { ja: { 'Hello, world!': 'こんにちは、世界!' } } ``` This is useful because you don't have to specify a translation for the string "Hello, world!" into English. In fact, you can even include template parameters in a key. Together with `formatFallbackMessages: true`, this lets you skip writing templates for your "base" language; the keys *are* your templates. ```javascript const messages = { ru: { 'Hello {name}': 'Здравствуйте {name}' } } const i18n = new VueI18n({ locale: 'ru', fallbackLocale: 'en', formatFallbackMessages: true, messages }) ``` When the template is as below: ```html

{{ $t('Hello {name}', { name: 'John' }}) }}

{{ $t('The weather today is {condition}!', { condition: 'sunny' }) }}

``` The following will be output: ```html

Здравствуйте, John

The weather today is sunny!

``` ================================================ FILE: vuepress/guide/formatting.md ================================================ # Formatting ## Named formatting Locale messages: ```js const messages = { en: { message: { hello: '{msg} world' } } } ``` Template: ```html

{{ $t('message.hello', { msg: 'hello' }) }}

``` Output: ```html

hello world

``` ## List formatting Locale messages: ```js const messages = { en: { message: { hello: '{0} world' } } } ``` Template: ```html

{{ $t('message.hello', ['hello']) }}

``` Output: ```html

hello world

``` List formatting also accepts array-like objects: ```html

{{ $t('message.hello', {'0': 'hello'}) }}

``` Output: ```html

hello world

``` ## HTML formatting :::warning Notice :warning: Dynamically localizing arbitrary HTML on your website can be very dangerous because it can easily lead to XSS vulnerabilities. Only use HTML interpolation on trusted content and never on user-provided content. We recommended using the [component interpolation](interpolation.md) feature. ::: :::warning Notice > :new: 8.11+ You can control the use of HTML formatting. see the detail `warnHtmlInMessage` constructor option and property API. ::: In some cases you might want to render your translation as an HTML message and not a static string. ```js const messages = { en: { message: { hello: 'hello
world' } } } ``` Template: ```html

``` Output (instead of the pre-formatted message ) ```html

hello world

``` ## Support ruby on rails i18n format Locale messages: ```js const messages = { en: { message: { hello: '%{msg} world' } } } ``` Template: ```html

{{ $t('message.hello', { msg: 'hello' }) }}

``` Output: ```html

hello world

``` ## Custom formatting Sometimes, you may need to translate using custom formatting (e.g. [ICU message syntax](http://userguide.icu-project.org/formatparse/messages)). You can do that with a custom formatter that implements the [Formatter Interface](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js#L145-L147). The following custom formatter with ES2015 class syntax: ```js // Custom Formatter implementation class CustomFormatter { constructor (options) { // ... } // // interpolate // // @param {string} message // string of list or named format. // e.g. // - named formatting: 'Hi {name}' // - list formatting: 'Hi {0}' // // @param {Object | Array} values // values of `message` interpolation. // passed values with `$t`, `$tc` and `i18n` functional component. // e.g. // - $t('hello', { name: 'kazupon' }) -> passed values: Object `{ name: 'kazupon' }` // - $t('hello', ['kazupon']) -> passed values: Array `['kazupon']` // - `i18n` functional component (component interpolation) // //

kazupon

//

how are you?

//
// -> passed values: Array (included VNode): // `[VNode{ tag: 'p', text: 'kazupon', ...}, VNode{ tag: 'p', text: 'how are you?', ...}]` // // @return {Array} // interpolated values. you need to return the following: // - array of string, when is using `$t` or `$tc`. // - array included VNode object, when is using `i18n` functional component. // interpolate (message, values) { // implement interpolation logic here // ... // return the interpolated array return ['resolved message string'] } } // register with `formatter` option const i18n = new VueI18n({ locale: 'en-US', formatter: new CustomFormatter(/* here the constructor options */), messages: { 'en-US': { // ... }, // ... } }) // Run! new Vue({ i18n }).$mount('#app') ``` You can check [the custom formatter official example](https://github.com/kazupon/vue-i18n/tree/dev/examples/formatting/custom). ================================================ FILE: vuepress/guide/hot-reload.md ================================================ # Hot reloading You can use Webpack's [Hot Module Replacement](https://webpack.js.org/concepts/hot-module-replacement/) (HMR) feature to watch for changes in localization files and hot reload changes into your application. ## Basic example If you only use a static set of locales, you can hot reload those locales explicitly: ```js import Vue from "vue" import VueI18n from "vue-i18n" import en from './en' import ja from './ja' const messages = { en, ja } // VueI18n instance const i18n = new VueI18n({ locale: 'en', messages }) // Run app const app = new Vue({ i18n, // ... }).$mount('#app') // Hot updates if (module.hot) { module.hot.accept(['./en', './ja'], function () { i18n.setLocaleMessage('en', require('./en').default) i18n.setLocaleMessage('ja', require('./ja').default) // Or the following hot updates via $i18n property // app.$i18n.setLocaleMessage('en', require('./en').default) // app.$i18n.setLocaleMessage('ja', require('./ja').default) }) } ``` ## Advanced example If you want to support a changing set of locales, you can hot reload those locales dynamically using `require.context`: ```js import Vue from "vue"; import VueI18n from "vue-i18n"; Vue.use(VueI18n); // Load all locales and remember context function loadMessages() { const context = require.context("./locales", true, /[a-z0-9-_]+\.json$/i); const messages = context .keys() .map((key) => ({ key, locale: key.match(/[a-z0-9-_]+/i)[0] })) .reduce( (messages, { key, locale }) => ({ ...messages, [locale]: context(key), }), {} ); return { context, messages }; } const { context, messages } = loadMessages(); // VueI18n instance const i18n = new VueI18n({ locale: "en", messages, }); // Run app const app = new Vue({ i18n, // ... }).$mount('#app'); // Hot updates if (module.hot) { module.hot.accept(context.id, () => { const { messages: newMessages } = loadMessages(); Object.keys(newMessages) .filter((locale) => messages[locale] !== newMessages[locale]) .forEach((locale) => { messages[locale] = newMessages[locale]; i18n.setLocaleMessage(locale, messages[locale]); }); }); } ``` ================================================ FILE: vuepress/guide/interpolation.md ================================================ # Component interpolation ## Basic Usage :::tip Support Version :new: 7.0+ ::: Sometimes, we need to localize with a locale message that was included in a HTML tag or component. For example: ```html

I accept xxx Terms of Service Agreement

``` In the above message, if you use `$t`, you will probably try to compose the following locale messages: ```js const messages = { en: { term1: 'I Accept xxx\'s', term2: 'Terms of Service Agreement' } } ``` And your localized template may look like this: ```html

{{ $t('term1') }}{{ $t('term2') }}

``` Output: ```html

I accept xxx Terms of Service Agreement

``` This is very cumbersome, and if you configure the `` tag in a locale message, there is a possibility of XSS vulnerabilities due to localizing with `v-html="$t('term')"`. You can avoid it using the `i18n` functional component. For example: ```html ``` ```js const messages = { en: { tos: 'Term of Service', term: 'I accept xxx {0}.' }, ja: { tos: '利用規約', term: '私は xxx の{0}に同意します。' } } const i18n = new VueI18n({ locale: 'en', messages }) new Vue({ i18n, data: { url: '/term' } }).$mount('#app') ``` the following output: ```html
``` About the above example, see the [example](https://github.com/kazupon/vue-i18n/tree/dev/examples/interpolation/places) The children of `i18n` functional component are interpolated with locale message of `path` prop. In the above example: :::v-pre `{{ $t('tos') }}` ::: is interpolated with `term` locale message. In the above example, the component interpolation follows the **list formatting**. The children of `i18n` functional component are interpolated by their order of appearance. You can choose the root container's node type by specifying a `tag` prop. If omitted, it defaults to `'span'`. You can also set it to the boolean value `false` to insert the child nodes directly without creating a root element. ## Slots syntax usage :::tip Support Version :new: 8.14+ ::: It's more convenient to use the named slots syntax. For example: ```html
``` ```js const messages = { en: { info: 'You can {action} until {limit} minutes from departure.', change: 'change your flight', refund: 'refund the ticket' } } const i18n = new VueI18n({ locale: 'en', messages }) new Vue({ i18n, data: { changeUrl: '/change', refundUrl: '/refund', changeLimit: 15, refundLimit: 30 } }).$mount('#app') ``` Outputs: ```html

You can change your flight until 15 minutes from departure.

``` In Vue 2.6 and later, you can use the following slots syntax in templates: ```html
``` :::warning Limitation :warning: In `i18n` component, slots props are not supported. ::: ## Places syntax usage :::danger Important!! In the next major version, the `place` and `places` props will be deprecated. Please switch to slots syntax. ::: :::tip Support Version :new: 7.2+ ::: :::warning Notice :warning: In `i18n` component, text content consisting of only white spaces will be omitted. ::: Named formatting is supported with the help of `place` attribute. For example: ```html
{{ changeLimit }} {{ $t('change') }}
``` ```js const messages = { en: { info: 'You can {action} until {limit} minutes from departure.', change: 'change your flight', refund: 'refund the ticket' } } const i18n = new VueI18n({ locale: 'en', messages }) new Vue({ i18n, data: { changeUrl: '/change', refundUrl: '/refund', changeLimit: 15, refundLimit: 30 } }).$mount('#app') ``` Outputs: ```html

You can change your flight until 15 minutes from departure.

``` :::warning Notice :warning: To use named formatting, all children of `i18n` component must have `place` attribute set. Otherwise it will fallback to list formatting. ::: If you still want to interpolate text content in named formatting, you could define `places` property on `i18n` component. For example: ```html ``` Outputs: ```html

You can refund your ticket until 30 minutes from departure.

``` ================================================ FILE: vuepress/guide/lazy-loading.md ================================================ # Lazy loading translations Loading all of your translation files at once is overkill and unnecessary. Lazy loading or asynchronously loading the translation files is really easy when using Webpack. Let´s assume we have a project directory similar to the one below: ``` our-cool-project -dist -src --routes --store --setup ---i18n-setup.js --lang ---en.js ---it.js ``` The `lang` folder is where all of our translation files reside. The `setup` folder is where our arbitrary setup files like the i18n-setup, global component inits, plugin inits and other reside. ```js //i18n-setup.js import Vue from 'vue' import VueI18n from 'vue-i18n' import messages from '@/lang/en' import axios from 'axios' Vue.use(VueI18n) export const i18n = new VueI18n({ locale: 'en', // set locale fallbackLocale: 'en', messages // set locale messages }) const loadedLanguages = ['en'] // our default language that is preloaded function setI18nLanguage (lang) { i18n.locale = lang axios.defaults.headers.common['Accept-Language'] = lang document.querySelector('html').setAttribute('lang', lang) return lang } export function loadLanguageAsync(lang) { // If the same language if (i18n.locale === lang) { return Promise.resolve(setI18nLanguage(lang)) } // If the language was already loaded if (loadedLanguages.includes(lang)) { return Promise.resolve(setI18nLanguage(lang)) } // If the language hasn't been loaded yet return import(/* webpackChunkName: "lang-[request]" */ `@/lang/${lang}.js`).then( messages => { i18n.setLocaleMessage(lang, messages.default) loadedLanguages.push(lang) return setI18nLanguage(lang) } ) } ``` In short we are creating a new VueI18n instance as we normally would. Then we are creating a `loadedLanguages` array that will keep track of our loaded languages. Next is the `setI18nLanguage` function that will actually change the language in our vueI18n instance, axios and where ever else is needed. The `loadLanguageAsync` function is what we will actually use to change the languages. Loading the new files is done via the `import` function, which is generously provided by Webpack and it allows us to load files dynamically, and because it uses promises we can easily wait for the loading to finish. You can learn more about the import function in the [Webpack documentation](https://webpack.js.org/guides/code-splitting/#dynamic-imports). Using the `loadLanguageAsync` function is straightforward. A common use case is inside a vue-router beforeEach hook. ```js router.beforeEach((to, from, next) => { const lang = to.params.lang loadLanguageAsync(lang).then(() => next()) }) ``` We could improve this by checking if the `lang` is actually supported by us or not, call `reject` so we can catch that in the `beforeEach` stopping the route transition. ================================================ FILE: vuepress/guide/locale.md ================================================ # Locale changing Normally, using the root Vue instance as the starting point, all child components are localized using the `locale` property of the `VueI18n` class as a reference. Sometimes you might want to dynamically change the locale. In that case you can change the value of the `locale` property of the `VueI18n` instance. ```js const i18n = new VueI18n({ locale: 'ja', // set locale ... }) // create root Vue instance new Vue({ i18n, ... }).$mount('#app') // change other locale i18n.locale = 'en' ``` Each component contains a `VueI18n` instance referenced as the `$i18n` property that can also be used to change the locale. Example: ```vue ``` :::warning Notice :warning: Locale changing is ignored for components that use `sync: false`. ::: :::warning Component vs. root scope :warning: Changing `$i18n.locale` inside a component does not update the root locale. If you rely on the root locale, for example when using [root fallbacks](./fallback.html), use `$root.$i18n.locale` instead of `$i18n.locale`. ::: ================================================ FILE: vuepress/guide/messages.md ================================================ # Locale messages syntax ## Structure Locale Messages syntax below: ```typescript // As Flowtype definition, Locale Messages syntax like BNF annotation type LocaleMessages = { [key: Locale]: LocaleMessageObject }; type LocaleMessageObject = { [key: Path]: LocaleMessage }; type LocaleMessageArray = LocaleMessage[]; type MessageContext = { list: (index: number) => mixed, named: (key: string) => mixed, linked: (key: string) => TranslateResult, values: any, // NOTE: not compatible for vue-i18n@v9.x path: string, // NOTE: not compatible for vue-i18n@v9.x formatter: Formatter, // NOTE: not compatible for vue-i18n@v9.x messages: LocaleMessages, // NOTE: not compatible for vue-i18n@v9.x locale: Locale // NOTE: not compatible for vue-i18n@v9.x }; type MessageFunction = (ctx: MessageContext) => string; type LocaleMessage = string | MessageFunction | LocaleMessageObject | LocaleMessageArray; type Locale = string; type Path = string; ``` Based on the above syntax, You can configure the following Locale Messages structure: ```json { "en": { // 'en' Locale "key1": "this is message1", // basic "nested": { // nested "message1": "this is nested message1" }, "errors": [ // array "this is 0 error code message", { // object in array "internal1": "this is internal 1 error message" }, [ // array in array "this is nested array error 1" ] ] }, "ja": { // 'ja' Locale // ... } } ``` In the above Locale Messages structure, You can translate using below key paths. ```html

{{ $t('key1') }}

{{ $t('nested.message1') }}

{{ $t('errors[0]') }}

{{ $t('errors[1].internal1') }}

{{ $t('errors[2][0]') }}

``` Output the following: ```html

this is message1

this is nested message1

this is 0 error code message

this is internal 1 error message

this is nested array error 1

``` ## Linked locale messages If there's a translation key that will always have the same concrete text as another one you can just link to it. To link to another translation key, all you have to do is to prefix its contents with an `@:` sign followed by the full name of the translation key including the namespace you want to link to. Locale messages the below: ```js const messages = { en: { message: { the_world: 'the world', dio: 'DIO:', linked: '@:message.dio @:message.the_world !!!!' } } } ``` Template: ```html

{{ $t('message.linked') }}

``` Output: ```html

DIO: the world !!!!

``` ### Formatting linked locale messages If the language distinguish cases of character, you may need control the case of the linked locale messages. Linked messages can be formatted with modifier `@.modifier:key` The below modifiers are available currently. * `upper`: Uppercase all characters in the linked message. * `lower`: Lowercase all characters in the linked message. * `capitalize`: Capitalize the first character in the linked message. Locale messages the below: ```javascript const messages = { en: { message: { homeAddress: 'Home address', missingHomeAddress: 'Please provide @.lower:message.homeAddress' } } } ``` ```html

{{ $t('message.missingHomeAddress') }}

``` Output the below: ```html

Please provide home address

``` You can add modifiers or overwrite the existing ones passing the `modifiers` options to the `VueI18n` constructor. ```javascript const i18n = new VueI18n({ locale: 'en', modifiers: { // Adding a new modifier snakeCase: (str) => str.split(' ').join('-') }, messages: { // ... }, }) ``` ### Grouping by brackets A translation key of linked locale message can also have the form of `@:(message.foo.bar.baz)` in which the link to another translation key is within brackets `()`. This can be useful if the link `@:message.something` is followed by period `.`, which otherwise would be part of the link and may not need to be. Locale messages: ```js const messages = { en: { message: { dio: 'DIO', linked: 'There\'s a reason, you lost, @:(message.dio).' } } } ``` Template: ```html

{{ $t('message.linked') }}

``` Output: ```html

There's a reason, you lost, DIO.

``` ## Message Function vue-i18n recommends using the string base on list or named format as locale messages when translating messages. There are situations however, where you really need the full programmatic power of JavaScript, due to the complex language syntax. So instead of string-based messages, you can use the **message function**. The following is a message function that returns a simple greeting: ```js const messages = { en: { greeting: (ctx) => 'hello!' } } ``` The use of the message function is very easy! You just specify the key of the message function with `$t` or `t`: ```html

{{ $t('greeting') }}

``` Output is the below: ```html

hello!

``` The message function outputs the message of the return value of the message function. ### Named formatting vue-i18n supports [named formatting](./formatting.md#named-formatting) as a string-based message format. vue-i18n interpolate the parameter values with `$t` or `t`, and it can be output it. You can do the same thing with the message function by using **message context**: here is the example of greeting: ```js const messages = { en: { greeting: (ctx) => `hello, ${ctx.named('name')}!` } } ``` Template: ```html

{{ $t('greeting', { name: 'DIO' }) }}

``` Output is the below: ```html

hello, DIO!

``` The message context has a named function. You need to specify the key that resolves the value specified with the named of `$t` or `t`. ### List formatting The use of the list format is similar to the named format described above. vue-i18n supports [list formatting](./formatting.md#list-formatting) as a string-based message format. vue-i18n interpolate the parameter values with `$t` or `t`, and it can be output it. You can do the same thing with the message function by using message context: here is the example of greeting: ```js const messages = { en: { greeting: (ctx) => `hello, ${ctx.list(0)}!` } } ``` Template: ```html

{{ $t('greeting', ['DIO']) }}

``` Output is the below: ```html

hello, DIO!

``` The message context has a list function. You need to specify the index that resolves the value specified with the list of `$t` or `t`. ### Limitation In the message function, the following functions, which are provided on a string basis, are not available via a message context: - Pluralization And also, the following props of message context is not compatible for vue-i18 v9.x or later: - `path` - `locale` - `messages` - `formatter` If you are using these message context props means that it will be difficult to migrate to vue-i18n v9.x or later. ================================================ FILE: vuepress/guide/number.md ================================================ # Number localization :::tip Support Version :new: 7.0+ ::: You can localize the number with your definition formats. Number formats the below: ```js const numberFormats = { 'en-US': { currency: { style: 'currency', currency: 'USD' } }, 'ja-JP': { currency: { style: 'currency', currency: 'JPY', currencyDisplay: 'symbol' } } } ``` As the above, you can define named number formats (e.g. `currency`, etc), and you need to use [the options with ECMA-402 Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat) After that, when using the locale messages, you need to specify the `numberFormats` option of the `VueI18n` constructor: ```js const i18n = new VueI18n({ numberFormats }) new Vue({ i18n }).$mount('#app') ``` Template the below: ```html

{{ $n(100, 'currency') }}

{{ $n(100, 'currency', 'ja-JP') }}

``` Output the below: ```html

$100.00

¥100

``` ## Custom formatting :::tip Support Version :new: 8.10+ ::: `$n` method returns resulting string with fully formatted number, which can only be used as a whole. In situations when you need to style some part of the formatted number (like fraction digits), `$n` is not enough. In such cases `` functional component will be of help. With a minimum set of properties, `` generates the same output as `$n`, wrapped into configured DOM element. The following template: ```html
``` will produce the below output: ```html
100 $100.00 ¥100
``` But the real power of this component comes into play when it is used with [scoped slots](https://vuejs.org/v2/guide/components-slots.html#Scoped-Slots). Let's say there is a requirement to render the integer part of the number with a bolder font. This can be achieved by specifying `integer` scoped slot element: ```html ``` Template above will result in the following HTML: ```html $100.00 ``` It is possible to specify multiple scoped slots at the same time: ```html ``` (this resulting HTML was formatted for better readability) ```html 1 , 234 00 ``` You can choose the root container's node type by specifying a `tag` prop. If omitted, it defaults to `'span'`. You can also set it to the boolean value `false` to insert the child nodes directly without creating a root element. Full list of the supported scoped slots as well as other `` properties can be found [on API page](../api/readme.md#i18n-n-functional-component). ================================================ FILE: vuepress/guide/pluralization.md ================================================ # Pluralization You can translate with pluralization. You must define the locale that have a pipe `|` separator, and define plurals in pipe separator. *Your template will need to use `$tc()` instead of `$t()`.* Locale messages below: ```js const messages = { en: { car: 'car | cars', apple: 'no apples | one apple | {count} apples' } } ``` Template below: ```html

{{ $tc('car', 1) }}

{{ $tc('car', 2) }}

{{ $tc('apple', 0) }}

{{ $tc('apple', 1) }}

{{ $tc('apple', 10, { count: 10 }) }}

``` Output below: ```html

car

cars

no apples

one apple

10 apples

``` ## Accessing the number via the pre-defined argument You don't need to explicitly give the number for pluralization. The number can be accessed within locale messages via pre-defined named arguments `{count}` and/or `{n}`. You can overwrite those pre-defined named arguments if necessary. Locale messages the below: ```js const messages = { en: { apple: 'no apples | one apple | {count} apples', banana: 'no bananas | {n} banana | {n} bananas' } } ``` Template below: ```html

{{ $tc('apple', 10, { count: 10 }) }}

{{ $tc('apple', 10) }}

{{ $tc('banana', 1, { n: 1 }) }}

{{ $tc('banana', 1) }}

{{ $tc('banana', 100, { n: 'too many' }) }}

``` Output below: ```html

10 apples

10 apples

1 banana

1 banana

too many bananas

``` ## Custom pluralization _This feature is available since version 8.0_ Such pluralization, however, does not apply to all languages (Slavic languages, for example, have different pluralization rules). In order to implement these rules you can pass an optional `pluralizationRules` object into `VueI18n` constructor options. Very simplified example using rules for Slavic languages (Russian, Ukrainian, etc.): ```js new VueI18n({ // Key - language to use the rule for, `'ru'`, in this case // Value - function to choose right plural form pluralizationRules: { /** * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)` * @param choicesLength {number} an overall amount of available choices * @returns a final choice index to select plural word by */ 'ru': function(choice, choicesLength) { // this === VueI18n instance, so the locale property also exists here if (choice === 0) { return 0; } const teen = choice > 10 && choice < 20; const endsWithOne = choice % 10 === 1; if (choicesLength < 4) { return (!teen && endsWithOne) ? 1 : 2; } if (!teen && endsWithOne) { return 1; } if (!teen && choice % 10 >= 2 && choice % 10 <= 4) { return 2; } return (choicesLength < 4) ? 2 : 3; } } }) ``` This would effectively give this: ```javascript const messages = { ru: { car: '0 машин | {n} машина | {n} машины | {n} машин', banana: 'нет бананов | {n} банан | {n} банана | {n} бананов' } } ``` Where the format is `0 things | things count ends with 1 | things count ends with 2-4 | things count ends with 5-9, 0 and teens (10-19)`. P.S. Slavic pluralization is difficult, you can read more about it [here](http://www.russianlessons.net/lessons/lesson11_main.php). Your template still needs to use `$tc()`, not `$t()`: ```html

{{ $tc('car', 1) }}

{{ $tc('car', 2) }}

{{ $tc('car', 4) }}

{{ $tc('car', 12) }}

{{ $tc('car', 21) }}

{{ $tc('banana', 0) }}

{{ $tc('banana', 4) }}

{{ $tc('banana', 11) }}

{{ $tc('banana', 31) }}

``` Which results in: ```html

1 машина

2 машины

4 машины

12 машин

21 машина

нет бананов

4 банана

11 бананов

31 банан

``` ### Default pluralization If your current locale is not found in a pluralization map, the [default](#pluralization) rule of the english language will be used. ================================================ FILE: vuepress/guide/sfc.md ================================================ # Single file components ## Basic Usage If you are building Vue component or Vue application using single file components, you can manage the locale messages `i18n` custom block. The following in [single file components example](https://github.com/kazupon/vue-i18n/tree/dev/examples/sfc): ```vue { "en": { "hello": "hello world!" }, "ja": { "hello": "こんにちは、世界!" } } ``` ## Installing vue-i18n-loader You need to install `vue-loader` and `vue-i18n-loader` to use `` custom blocks. While [vue-loader](https://github.com/vuejs/vue-loader) most likely is already used in your project if you are working with single file components, you must install [vue-i18n-loader](https://github.com/kazupon/vue-i18n-loader) additionally: ```sh npm i --save-dev @intlify/vue-i18n-loader ``` ## Webpack For Webpack the configuration below is required: for vue-loader v15 or later: ```js module.exports = { // ... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', }, { resourceQuery: /blockType=i18n/, type: 'javascript/auto', loader: '@intlify/vue-i18n-loader' } // ... ] }, // ... } ``` for vue-loader v14: ```js module.exports = { // ... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { // you need to specify `i18n` loaders key with `vue-i18n-loader` (https://github.com/intlify/vue-i18n-loader) i18n: '@intlify/vue-i18n-loader' } } }, // ... ] }, // ... } ``` ## Vue CLI 3.0 [Vue CLI 3.0](https://github.com/vuejs/vue-cli) hides the webpack configuration, so, if we want to add support to the `` tag inside a single file component we need to modify the existing configuration. In order to do that we have to create a `vue.config.js` at the root of our project. Once we have done that, we have to include the following: for vue-loader v15 or later: ```js module.exports = { chainWebpack: config => { config.module .rule("i18n") .resourceQuery(/blockType=i18n/) .type('javascript/auto') .use("i18n") .loader("@intlify/vue-i18n-loader") .end(); } } ``` for vue-loader v14: ```js const merge = require('deepmerge') module.exports = { chainWebpack: config => { config.module .rule('vue') .use('vue-loader') .tap(options => merge(options, { loaders: { i18n: '@intlify/vue-i18n-loader' } }) ) } } ``` _Don 't forget to install [deepmerge](https://github.com/KyleAMathews/deepmerge)! (`npm i deepmerge -D` or `yarn add deepmerge -D`)_ If you want to read more about modifying the existing configuration [click here](https://cli.vuejs.org/guide/webpack.html). ## Laravel-Mix for Laravel-mix 4 with vue-loader v15 or later: ```js // Extend Mix with the "i18n" method, that loads the vue-i18n-loader mix.extend( 'i18n', new class { webpackRules() { return [ { resourceQuery: /blockType=i18n/, type: 'javascript/auto', loader: '@intlify/vue-i18n-loader', }, ]; } }(), ); // Make sure to call the .i18n() (to load the loader) before .js(..., ...) mix.i18n() .js( 'resources/js/App.js', 'public/js/app.js' ) ... ``` for Laravel-mix 2 with vue-loader v14: As of [V2.1](https://github.com/JeffreyWay/laravel-mix/releases/tag/v2.1) of Laravel-mix, you can add custom rules via mix.extend(). Laravel mix already has its own rules for handling .vue files. To add the `vue-i18n-loader`, add the following to `webpack.mix.js` ```js // The below code will inject i18n Kazupon/vue-18-loader as a loader for .vue files. mix.extend( 'i18n', function( webpackConfig, ...args ) { webpackConfig.module.rules.forEach( ( module ) => { // Search for the "vue-loader" component, which handles .vue files. if( module.loader !== 'vue-loader' ) { return; } // Within this module, add the vue-i18n-loader for the i18n tag. module.options.loaders.i18n = '@intlify/vue-i18n-loader'; } ); } ); // Make sure to call .i18n() before .js(..., ...) mix.i18n() .js( 'resources/assets/js/App.js', 'public/js/app.js' ) ... ``` ## YAML loading `i18n` custom blocks need to specify `JSON` format, also you can use `YAML` format by using pre-loader feature of `vue-loader`. the `i18n` custom blocks below of `YAML` format: ```vue en: hello: "hello world!" ja: hello: "こんにちは、世界!" ``` Webpack conf the below: for vue-loader v15 or later: ```js // Vue CLI 3.0 module.exports = { chainWebpack: config => { config.module .rule("i18n") .resourceQuery(/blockType=i18n/) .type('javascript/auto') .use("i18n") .loader("@intlify/vue-i18n-loader") .end() .use('yaml') .loader('yaml-loader') .end() } } ``` for vue-loader v14: ```js module.exports = { // ... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { preLoaders: { i18n: 'yaml-loader' }, loaders: { i18n: '@intlify/vue-i18n-loader' } } }, // ... ] }, // ... } ``` ## Multiple custom blocks You can use locale messages with multiple `i18n` custom blocks. ```vue { "en": { "hello": "hello world!" }, "ja": { "hello": "こんにちは、世界!" } } ``` In the above, first custom block load the common locale message with `src` attribute, second custom block load the locale message that is defined only at single file component. These locale messages will be merged as locale message of component. In this way, multiple custom blocks useful when want to be used as module. ## Scoped style When using `vue-i18n` with `scoped style`, it's important to remember to use a [deep selector](https://vue-loader.vuejs.org/guide/scoped-css.html#child-component-root-elements) for styling an element __*inside*__ the translation string. For example: __Translation contains only text__ (Work without deep selector) ```vue { "en": { "hello": "hello world!" }, "ja": { "hello": "こんにちは、世界" } } ``` __Translation with HTML element__ (Must use deep selector) ```vue { "en": { "hello": "helloworld!" }, "ja": { "hello": "こんにちは、世界!" } } ``` ## Custom blocks in functional component If the single file components have the template using a functional component, and you had been defined `i18n` custom blocks, note you cannot localize using locale messages. For example, the following code cannot localize with the locale message of `i18n` custom block. ```vue { "en": { "hello": "hello world" }, "ja": { "hello": "こんにちは、世界" } } ``` ================================================ FILE: vuepress/guide/tooling.md ================================================ # Tooling To support the i18n of Vue applications, some tools are officially provided. There are also tools from third vendors integrating Vue I18n. ## Official tooling ### Vue CLI Plugin [vue-cli-plugin-i18n](https://github.com/kazupon/vue-cli-plugin-i18n) is officially provided as the Vue CLI Plugin. With this plugin, you can setup the i18n environment for the Vue application, and support the i18n development environment. ### Nuxt Module [nuxt-i18n](https://github.com/nuxt-community/nuxt-i18n/) is corresponding Nuxt.js module. ### Webpack loader [vue-i18n-loader](https://github.com/kazupon/vue-i18n-loader) is an officially provided webpack loader. With this loader, you can use the `i18n` custom block in the Single file components. In about `i18n` custom block, see the [Single file components section](./sfc.md) ### ESLint Plugin [eslint-plugin-vue-i18n](https://intlify.github.io/eslint-plugin-vue-i18n/) is ESLint plugin for Vue I18n. It easily integrates some localization lint features to your Vue.js Application. ### Extensions [vue-i18n-extensions](https://github.com/kazupon/vue-i18n-extensions) provides some extensions for Vue I18n. You can use this extension to enable SSR and improve i18n performance. ## 3rd party tooling ### BabelEdit [BabelEdit](https://www.codeandweb.com/babeledit) is translation editor for web apps. BabelEdit can translate `json` files, and it can also translate `i18n` custom block of Single-file components. Read more about BabelEdit in [tutorial page](https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-vue-app-with-vue-i18n). ### i18n Ally [i18n Ally](https://marketplace.visualstudio.com/items?itemName=lokalise.i18n-ally) is i18n extension for VSCode. i18n Ally give awesome DX for your i18n development. Read more about i18n Ally in [README](https://github.com/lokalise/i18n-ally/blob/main/README.md). ### i18nPlugin (intellij platform) [i18nPlugin](https://github.com/nyavro/i18nPlugin) Intellij idea i18next support plugin ( [Jetbrains plugin page ](https://plugins.jetbrains.com/plugin/12981-i18n-support)). Plugin for i18n typescript/javascript/PHP. Supports vue-i18n. To enable vue-i18n support go to settings -> Tools -> i18n Plugin configuration and check "Vue-i18n". You need set vue locales directory (locales by default). ### vue-i18n-extract [vue-i18n-extract](https://github.com/pixari/vue-i18n-extract) performs static analysis on a Vue.js project based on vue-i18n and reports the following information: - list of all the **unused vue-i18n keys** (entries found in the language files but not used in the project) - list of all the **missing keys** (entries fond in the project but not in the language files) It's possible to show the output in the console or to write it in a json file. The missing keys can be also automatically added to the given language files. ================================================ FILE: vuepress/installation.md ================================================ # Installation ## Compatibility Note - Vue.js `2.0.0`+ ## Direct Download / CDN [unpkg.com](https://unpkg.com) provides NPM-based CDN links. The above link will always point to the latest release on NPM. You can also use a specific version/tag via URLs like Include vue-i18n after Vue and it will install itself automatically: ```html ``` ## NPM ```sh npm install vue-i18n@8 ``` ## Yarn ```sh yarn add vue-i18n@8 ``` When using with a module system, you must explicitly install the `vue-i18n` via `Vue.use()`: ```javascript import Vue from 'vue' import VueI18n from 'vue-i18n' Vue.use(VueI18n) ``` You don't need to do this when using global script tags ` ``` :::warning Aviso :warning: As alterações de localização são ignoradas pelos componentes com a opção `sync: false`. ::: :::warning Componente vs. escopo root :warning: Alterarando `$i18n.locale` dentro do componente não altera a localização root. Se você está contando com a localização root, por exemplo, ao usar [root fallback](./fallback.html), use `$root.$I18n.locale` ao vez de `$i18n.locale`. ::: ================================================ FILE: vuepress/pt/guide/messages.md ================================================ # Sintaxe mensagens locais ## Estrutura Sintaxe local das mensagens: ```typescript // Como definição do Flowtype, a sintaxe das mensagens de tradução é semelhante à anotação BNF type LocaleMessages = { [key: Locale]: LocaleMessageObject } type LocaleMessageObject = { [key: Path]: LocaleMessage } type LocaleMessageArray = LocaleMessage[] type MessageContext = { list: (index: number) => mixed, named: (key: string) => mixed, linked: (key: string) => TranslateResult, values: any, path: string, formatter: Formatter, messages: LocaleMessages, locale: Locale }; type MessageFunction = (ctx: MessageContext) => string; type LocaleMessage = string | MessageFunction | LocaleMessageObject | LocaleMessageArray; type Locale = string type Path = string ``` Com base na sintaxe acima, você pode configurar a seguinte estrutura de mensagens locais: ```json { // localização 'pt' "pt": { "key1": "esta é a mensagem 1", // uso comum "nested": { // aninhado "message1": "esta é a mensagem aninhada 1" }, "errors": [ // array "esta é a mensagem de código de erro 0", { // um objeto em array "internal1": "esta é uma mensagem de código de erro interno 1" }, [ // array em array "este é o erro de array aninhado 1" ] ] }, // localização 'en' "en": { // ... } } ``` Na estrutura de mensagens locais acima, você pode traduzir usando os caminhos-chave abaixo. ```html

{{ $t('key1') }}

{{ $t('nested.message1') }}

{{ $t('errors[0]') }}

{{ $t('errors[1].internal1') }}

{{ $t('errors[2][0]') }}

``` O resultado será o seguinte: ```html

esta é a mensagem 1

esta é a mensagem aninhada 1

esta é a mensagem de código de erro 0

esta é uma mensagem de código de erro interno 1

este é o erro de array aninhado 1

``` ## Mensagens de localização relacionadas Se houver uma chave de tradução que sempre terá o mesmo texto concreto igual outra, você pode simplesmente criar um link para essa. Para vincular a outra chave de tradução, tudo que você precisa fazer é prefixar seu conteúdo com um sinal `@:` seguido pelo nome completo da chave de tradução incluindo o namespace ao qual deseja vincular. Mensagens de localização a seguir: ```js const messages = { en: { message: { the_world: 'the world', dio: 'DIO:', linked: '@:message.dio @:message.the_world !!!!' } } } ``` Template: ```html

{{ $t('message.linked') }}

``` O resultado será o seguinte: ```html

DIO: the world !!!!

``` ### Formatando mensagens de localização relacionadas Se o idioma distinguir casos de caracteres, você pode precisar controlar o caso das mensagens de localização relacionadas. Mensagens relacionadas podem ser formatadas com o modificador `@.modifier:key` Mensagens de localização a seguir: * `upper`: Letras maiúsculas em todos os caracteres na mensagem vinculada. * `lower`: Letras minúsculas em todos os caracteres na mensagem vinculada. * `capitalize`: Primeiro caractere em maiúsculo da mensagem vinculada. Mensagens de localização: ```js const messages = { en: { message: { homeAddress: 'Home address', missingHomeAddress: 'Please provide @.lower:message.homeAddress' } }, pt: { message: { homeAddress: 'Endereço residencial', missingHomeAddress: 'Por favor, providencie o @.lower:message.homeAddress' } } } ``` ```html

{{ $t('message.missingHomeAddress') }}

``` O resultado será o seguinte: ```html

Por favor, providencie o Endereço residencial

``` Você pode adicionar modificadores ou sobrescrever os existentes passando as opções de `modificadores` para o construtor `VueI18n`. ```js const i18n = new VueI18n({ locale: 'pt', modifiers: { // Adicionando um novo modificador snakeCase: str => str.split(' ').join('-') }, messages: { // ... }, }) ``` ### Agrupando com parêntese Uma chave de tradução de uma mensagem também pode ser especificada com `@:(message.foo.bar.baz)`, onde a referência a outra chave de tradução está entre parêntese `()`. Isso pode ser necessário se um ponto `.` for exigido após um link para outra mensagem `@:message.something`, que de outra forma seria considerado parte do link. Mensagens de localização: ```js const messages = { en: { message: { dio: 'DIO', linked: "There's a reason, you lost, @:(message.dio)." } }, pt: { message: { dio: 'DIO', linked: "Há uma razão pela qual você falhou, @:(message.dio)." } } } ``` Template: ```html

{{ $t('message.linked') }}

``` O resultado será o seguinte: ```html

Há uma razão pela qual você falhou, DIO.

``` ## Mensagem com função vue-i18n recomenda o uso de strings para formatação de lista ou formatação nomeada como mensagem de localização ao traduzir as mensagens. No entanto, existem situações em que, devido à sintaxe complexa da linguagem, todo o poder do JavaScript é necessário. Nesse caso, em vez de mensagens de string, você pode usar **uma mensagem com função**. A função abaixo retorna uma saudação: ```js const messages = { en: { greeting: (ctx) => 'Hello!' }, pt: { greeting: (ctx) => 'Olá!' } } ``` Usar a função da mensagem é fácil! Você só precisa especificar a chave usando `$t` ou `t`: ```html

{{ $t('greeting') }}

``` O resultado será o seguinte: ```html

Olá!

``` O resultado de retorno da função é usado para a mensagem. ### Formatação nomeada vue-i18n suporta [formatação nomeada](./formatting.md#named-formatting) como um formato de mensagem baseado em string. vue-i18n interpola os valores dos parâmetros com `$t` ou `t`, e os retorna. O mesmo pode ser feito com a função da mensagem usando o **contexto de mensagem**: Aqui está o exemplo de saudação: ```js const messages = { en: { greeting: (ctx) => `Hello, ${ctx.named('name')}!` }, pt: { greeting: (ctx) => `Olá, ${ctx.named('name')}!` } } ``` Template: ```html

{{ $t('greeting', { name: 'DIO' }) }}

``` O resultado será o seguinte: ```html

Olá, DIO!

``` O contexto da mensagem fornece acesso à função `named`. Você deve especificar a chave especificada para `$t` ou `t`, que resolverá com o valor necessário. ### Formatação de lista O uso da formatação de lista é semelhante ao formatação nomeada descrito acima. vue-i18n suporta [formatação de lista](./formatting.md#list-formatting) para mensagens de string. vue-i18n interpola os valores dos parâmetros com `$t` ou `t`, e os retorna. O mesmo pode ser feito com a função da mensagem usando o **contexto de mensagem**: Aqui está o exemplo de saudação: ```js const messages = { en: { greeting: (ctx) => `Hello, ${ctx.list(0)}!` }, pt: { greeting: (ctx) => `Olá, ${ctx.list(0)}!` } } ``` Template: ```html

{{ $t('greeting', ['DIO']) }}

``` O resultado será o seguinte: ```html

Olá, DIO!

``` O contexto da mensagem fornece acesso à função `list`. Você deve especificar a chave especificada para `$t` ou `t`, que resolverá com o valor necessário. ### Limitação Em uma função para mensagem, os seguintes recursos, que estão disponíveis em uma versão de string, não estarão disponíveis por meio do contexto da mensagem: - Mensagens de localidade vinculadas - Pluralização ================================================ FILE: vuepress/pt/guide/number.md ================================================ # Localização de números :::tip Suporta a versão :new: 7.0+ ::: Você pode localizar números com seus formatos de definição. Formato de exemplo para números: ```js const numberFormats = { 'en-US': { currency: { style: 'currency', currency: 'USD' } }, 'pt-BR': { currency: { style: 'currency', currency: 'BRL', currencyDisplay: 'symbol' } } } ``` Conforme declarado acima, você pode especificar formatos numéricos (por exemplo, `currency` para moeda) usando opções [ECMA-402 Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat). Depois disso, para poder usar este formato em mensagens de localização, você precisa definir a opção `numberFormats` do construtor na instância `VueI18n`: ```js const i18n = new VueI18n({ numberFormats }) new Vue({ i18n }).$mount('#app') ``` Template: ```html

{{ $n(100, 'currency') }}

{{ $n(100, 'currency', 'pt-BR') }}

``` O resultado será o seguinte: ```html

$100.00

100,00 ₽

``` ## Formatação personalizada :::tip Suporta a versão :new: 8.10+ ::: O método `$n` retorna o resultado como uma string numérica totalmente formatada que só pode ser usada em sua totalidade. Nos casos em que você precisa estilizar alguma parte de um número formatado (por exemplo, uma parte fracionária), `$n` não será suficiente. Nesses casos, é necessário usar o componente funcional `` será útil. Com um conjunto mínimo de propriedades, `` gera o mesmo resultado que `$n` envolvido em um elemento DOM configurado. Template: ```html
``` O resultado será o seguinte: ```html
100 $100.00 R$ 100,00
``` O verdadeiro poder deste componente entra em ação quando é usado com [slots com escopo](https://br.vuejs.org/v2/guide/components-slots.html#Slots-com-Escopo-Definido). Digamos que haja um requisito para exibir a parte inteira de um número em negrito. Isso pode ser obtido especificando `integer` no elemento do slot com escopo: ```html {{ slotProps.integer }} ``` O resultado será o seguinte: ```html $100.00 ``` É possível especificar vários slots com escopo ao mesmo tempo: ```html {{ slotProps.currency }} {{ slotProps.integer }} {{ slotProps.group }} {{ slotProps.fraction }} ``` (O HTML de resultado abaixo é formatado para melhor legibilidade) ```html 1 , 234 00 ``` Você pode especificar o tipo do elemento raiz usando o parâmetro de entrada `tag`. Se nenhum parâmetro de entrada for especificado, o padrão é `'span'`. Você também pode defini-lo com o valor booleano `false` para inserir os nós filhos diretamente sem criar um elemento raiz. A lista completa dos slots de escopo suportados, bem como outras propriedades ``, pode ser encontradas [na página da API](../api/readme.md#i18n-n-functional-component). ================================================ FILE: vuepress/pt/guide/pluralization.md ================================================ # Pluralização Você pode usar pluralização para mensagens traduzidas. Para fazer isso, precisa definir a localidade e especificar as strings de tradução para os diferentes casos por meio do separador `|`. *Seu template precisará usar `$tc()` em vez de `$t()`.* Mensagens locais abaixo: ```js const messages = { en: { car: 'car | cars', apple: 'no apples | one apple | {count} apples' }, pt: { car: 'carro | carros', apple: 'sem maçãs | uma maçã | {count} maçãs' } } ``` Template: ```html

{{ $tc('car', 1) }}

{{ $tc('car', 2) }}

{{ $tc('apple', 0) }}

{{ $tc('apple', 1) }}

{{ $tc('apple', 10, { count: 10 }) }}

``` O resultado será o seguinte: ```html

carro

carros

sem maçãs

uma maçã

10 maçãs

``` ## Acessando o número por meio do argumento predefinido Não há necessidade de passar explicitamente o número para pluralização. Em mensagens de localização, um número está disponível através dos argumentos nomeados `{count}` e/ou `{n}`. Você pode substituí-los, se desejar. Mensagens locais abaixo: ```js const messages = { en: { apple: 'no apples | one apple | {count} apples', banana: 'no bananas | {n} banana | {n} bananas' }, pt: { apple: 'sem maçãs | uma maçã | {count} maçãs' banana: 'sem bananas | {n} banana | {n} bananas' } } ``` Template: ```html

{{ $tc('apple', 10, { count: 10 }) }}

{{ $tc('apple', 10) }}

{{ $tc('banana', 1, { n: 1 }) }}

{{ $tc('banana', 1) }}

{{ $tc('banana', 100, { n: 'Muitas' }) }}

``` O resultado será o seguinte: ```html

10 maçãs

10 maçãs

1 banana

1 banana

Muitas bananas

``` ## Pluralização personalizadas Essa pluralização, entretanto, não se aplica a todas as línguas (as línguas eslavas, por exemplo, têm regras de pluralização diferentes). Para implementar essas regras, você pode passar um objeto `pluralizationRules` opcional para as opções do construtor `VueI18n`. Um exemplo simplificado para idiomas eslavos (russo, ucraniano e outros): ```js new VueI18n({ // Key - idioma para usar a regra, `'ru'`, neste caso // Value - função para escolher a forma plural correta pluralizationRules: { /** * @param choice {number} um índice de escolha dado pela entrada de $tc: `$tc('path.to.rule', choiceIndex)` * @param choicesLength {number} quantidade geral de opções disponíveis * @returns índice final para selecionar as palavras no plural */ 'ru': function(choice, choicesLength) { // this === Instância VueI18n, então a propriedade locale também existe aqui if (choice === 0) { return 0; } const teen = choice > 10 && choice < 20; const endsWithOne = choice % 10 === 1; if (choicesLength < 4) { return (!teen && endsWithOne) ? 1 : 2; } if (!teen && endsWithOne) { return 1; } if (!teen && choice % 10 >= 2 && choice % 10 <= 4) { return 2; } return (choicesLength < 4) ? 2 : 3; } } }) ``` Essa implementação permitirá que você use: ```js const messages = { ru: { car: '0 машин | {n} машина | {n} машины | {n} машин', banana: 'нет бананов | {n} банан | {n} банана | {n} бананов' } } ``` Onde o formato é `0 coisas | número de itens termina em 1 | o número de coisas que termina em 2-4 | o número de coisas que termina com 5-9, 0 e o número coisas que termina de 11 a 19`. P.S. A pluralização eslava é complexa, você pode ler mais sobre isso [aqui](http://www.russianlessons.net/lessons/lesson11_main.php). No template, você ainda precisa usar `$tc()` em vez de `$t()`: ```html

{{ $tc('car', 1) }}

{{ $tc('car', 2) }}

{{ $tc('car', 4) }}

{{ $tc('car', 12) }}

{{ $tc('car', 21) }}

{{ $tc('banana', 0) }}

{{ $tc('banana', 4) }}

{{ $tc('banana', 11) }}

{{ $tc('banana', 31) }}

``` O resultado será o seguinte: ```html

1 машина

2 машины

4 машины

12 машин

21 машина

нет бананов

4 банана

11 бананов

31 банан

``` ### Pluralização padrão Se nenhuma regra de pluralização for fornecida para a localidade em uso, a regra [padrão](#pluralizacao) do idioma inglês será usada. ================================================ FILE: vuepress/pt/guide/sfc.md ================================================ # Componentes de arquivo único ## Uso básico Se você estiver construindo um componente ou aplicativo Vue usando componentes de arquivo único, você pode gerenciar as mensagens de localização usando um bloco `i18n` personalizado. Código de componente com [exemplo de componentes de arquivo único](https://github.com/kazupon/vue-i18n/tree/dev/examples/sfc): ```vue { "en": { "hello": "Hello World!" }, "pt": { "hello": "Olá Mundo!" } } ``` ## Instalação vue-i18n-loader Você precisa instalar `vue-loader` e `vue-i18n-loader` devido ao uso de blocos personalizados ``. Embora [vue-loader](https://github.com/vuejs/vue-loader) provavelmente já seja usado em seu projeto se você estiver trabalhando com componentes de arquivo único, mas você vai precisar instalar [vue-i18n-loader](https://github.com/kazupon/vue-i18n-loader) adicionalmente: ```bash npm i --save-dev @kazupon/vue-i18n-loader ``` ## Webpack O Webpack requer a seguinte configuração: Para vue-loader v15 ou posterior: ```js module.exports = { // ... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader' }, { resourceQuery: /blockType=i18n/, type: 'javascript/auto', loader: '@kazupon/vue-i18n-loader' } // ... ] } // ... } ``` Para vue-loader v14: ```js module.exports = { // ... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { // você precisa especificar a chave do carregador `i18n` com o `vue-i18n-loader` // (https://github.com/kazupon/vue-i18n-loader) i18n: '@kazupon/vue-i18n-loader' } } } // ... ] } // ... } ``` ## Vue CLI 3.0 [Vue CLI 3.0](https://github.com/vuejs/vue-cli) ocultar a configuração do Webpack, portanto, para adicionar suporte para tags `` em componentes de arquivo único, precisamos modificar a configuração existente. Para fazer isso, temos que criar um `vue.config.js` na raiz do nosso projeto. Depois de fazer isso, devemos incluir o seguinte código: Para vue-loader v15 ou posterior: ```js module.exports = { chainWebpack: config => { config.module .rule('i18n') .resourceQuery(/blockType=i18n/) .type('javascript/auto') .use('i18n') .loader('@kazupon/vue-i18n-loader') .end() } } ``` Para vue-loader v14: ```js const merge = require('deepmerge') module.exports = { chainWebpack: config => { config.module .rule('vue') .use('vue-loader') .tap(options => merge(options, { loaders: { i18n: '@kazupon/vue-i18n-loader' } }) ) } } ``` _Não se esqueça de instalar o [deepmerge](https://github.com/KyleAMathews/deepmerge)! (`npm i deepmerge -D` ou `yarn add deepmerge -D`)_ Você pode aprender mais sobre as possibilidades de modificar a configuração do Webpack existente [neste guia](https://cli.vuejs.org/guide/webpack.html). ## Laravel-Mix Para Laravel-mix 4 com vue-loader v15 ou posterior: ```js // Estenda o Mix usando o método "i18n", que carrega o vue-i18n-loader mix.extend( 'i18n', new class { webpackRules() { return [ { resourceQuery: /blockType=i18n/, type: 'javascript/auto', loader: '@kazupon/vue-i18n-loader', }, ]; } }(), ); // Certifique-se de chamar .i18n() para carregar o loader antes do .js(..., ...) mix.i18n() .js( 'resources/js/App.js', 'public/js/app.js' ) ... ``` para Laravel-mix 2 com vue-loader v14: No Laravel-mix, começando na versão [V2.1](https://github.com/JeffreyWay/laravel-mix/releases/tag/v2.1), você pode adicionar regras personalizadas usando `mix.extend()`. O Laravel-mix já possui suas próprias regras para lidar com arquivos `.vue`. Para adicionar `vue-i18n-loader`, no arquivo `webpack.mix.js` adicione o seguinte código: ```js // O código a seguir injetará o i18n Kazupon/vue-18-loader como o loader de arquivos .vue mix.extend( 'i18n', function( webpackConfig, ...args ) { webpackConfig.module.rules.forEach( ( module ) => { // Procure o componente "vue-loader", que processa os arquivos .vue if( module.loader !== 'vue-loader' ) { return; } // Neste módulo, adicione o vue-i18n-loader para a tag i18n. module.options.loaders.i18n = '@kazupon/vue-i18n-loader'; } ); } ); // Certifique-se de chamar .i18n() para carregar o loader antes de .js(..., ...) mix.i18n() .js( 'resources/assets/js/App.js', 'public/js/app.js' ) ... ``` ## Carregando YAML Os blocos personalizados `i18n` podem ser especificados no formato `JSON` ou `YAML` usando o recurso de pré-carregador do `vue-loader`. Os blocos personalizados `i18n` no formato `YAML`: ```vue en: hello: "Hello World!" pt: hello: "Olá Mundo!" ``` Configuração do Webpack: Para vue-loader v15 ou posterior: ```js // Vue CLI 3.0 module.exports = { chainWebpack: config => { config.module .rule('i18n') .resourceQuery(/blockType=i18n/) .type('javascript/auto') .use('i18n') .loader('@kazupon/vue-i18n-loader') .end() .use('yaml') .loader('yaml-loader') .end() } } ``` Para vue-loader v14: ```js module.exports = { // ... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { preLoaders: { i18n: 'yaml-loader' }, loaders: { i18n: '@kazupon/vue-i18n-loader' } } } // ... ] } // ... } ``` ## Vários blocos personalizados Você pode usar mensagens de localização com vários blocos personalizados `i18n`. ```vue { "en": { "hello": "Hello World!" }, "pt": { "hello": "Olá Mundo!" } } ``` No exemplo acima, o primeiro bloco personalizado carrega as mensagens de localização genérica usando o atributo `src`, o segundo bloco personalizado carrega as mensagens de localização que são definidas apenas neste componente de arquivo único. Todos eles serão mesclados com mensagens de localização de componentes. Desta forma, vários blocos personalizados são úteis quando usados ​​como módulos. ## Estilos Locais Ao usar `vue-i18n` com estilos locais `style scoped`, é importante lembrar de usar [deep selector](https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors) para estilizar o elemento __*dentro*__ da string de tradução. Por exemplo: __Quando a tradução contém apenas texto__ (funciona sem deep selector) ```vue { "en": { "hello": "Hello World!" }, "pt": { "hello": "Olá Mundo!" } } ``` __Tradução que contém elemento HTML__ (deve usar deep selector) ```vue { "en": { "hello": "Hello World!" }, "pt": { "hello": "Olá Mundo!" } } ``` ## Blocos personalizados em componente funcional Se os componentes de arquivo único têm o template usando um componente funcional, e você definiu blocos personalizados `i18n`, observe que você não pode localizar usando mensagens de local. Por exemplo, o código a seguir não pode usar mensagens de localização do bloco `i18n`. ```vue { "en": { "hello": "Hello World" }, "pt": { "hello": "Olá Mundo" } } ``` ================================================ FILE: vuepress/pt/guide/tooling.md ================================================ # Ferramentas Para suportar os aplicativos i18n do Vue, algumas ferramentas são fornecidas oficialmente. Existem também ferramentas de terceiros que integram o Vue I18n. ## Ferramentas oficiais ### Plugin para Vue CLI [vue-cli-plugin-i18n](https://github.com/kazupon/vue-cli-plugin-i18n) é o plugin oficial para o Vue CLI. Com este plugin, você pode configurar um ambiente i18n para seu aplicativo Vue e oferecer suporte ao ambiente de desenvolvimento i18n. ### Módulo para Nuxt [nuxt-i18n](https://github.com/nuxt-community/nuxt-i18n/) é o módulo correspondente para Nuxt.js. ### Loader para Webpack [vue-i18n-loader](https://github.com/kazupon/vue-i18n-loader) — é o loader para webpack oficial. Com este loader é possível usar blocos `i18n` personalizados em componentes de arquivo único. Para obter mais informações sobre blocos `i18n` personalizados, consulte [Componentes de arquivo único](./sfc.md) ### Plugin para ESLint [eslint-plugin-vue-i18n](https://intlify.github.io/eslint-plugin-vue-i18n/) - Plug-in ESLint para Vue I18n. Permite que você integre facilmente alguns recursos de localização do lint ao seu aplicativo Vue.js. ### Extensões [vue-i18n-extensions](https://github.com/kazupon/vue-i18n-extensions) fornece algumas extensões para Vue I18n. Você pode usar esta extensão para habilitar o SSR e melhorar o desempenho do i18n. ## Ferramentas de terceiros ### BabelEdit [BabelEdit](https://www.codeandweb.com/babeledit) é um editor de tradução para aplicativos da web. O BabelEdit pode traduzir arquivos `json` e também pode trabalhar com blocos personalizados `i18n` de componentes de arquivo único. Mais informações sobre o BabelEdit podem ser encontradas na página de [introdução](https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-vue-app-with-vue-i18n). ### i18n Ally [i18n Ally] (https://marketplace.visualstudio.com/items?itemName=antfu.i18n-ally) é uma extensão i18n para VSCode. i18n Ally oferece um DX(Experiência do desenvolvedor) incrível para o desenvolvimento de i18n. Você pode aprender mais sobre a extensão i18n Ally em [README](https://github.com/antfu/i18n-ally/blob/master/README.md). ### i18nPlugin (plataforma intellij) [i18nPlugin](https://github.com/nyavro/i18nPlugin) — Plugin de suporte para idea Intellij i18next ([Página do plugin Jetbrains](https://plugins.jetbrains.com/plugin/12981-i18n-support)). Plugin para i18n typescript/javascript/PHP. Suporta vue-i18n. Para habilitar o suporte a vue-i18n vá em configurações -> Ferramentas -> Configuração do plugin i18n e selecione "Vue-i18n". É necessário instalar os diretórios com os arquivos de tradução (tradução por padrão). ### vue-i18n-extract [vue-i18n-extract](https://github.com/pixari/vue-i18n-extract) faz uma análise estática de um projeto Vue.js com base em vue-i18n e relata as seguintes informações: - lista de todas as **chaves vue-i18n não utilizadas** (entradas encontradas nos arquivos de idioma, mas não utilizadas no projeto) - lista de todas as **chaves ausentes** (entradas encontradas no projeto, mas não nos arquivos de idioma) É possível mostrar a saída no console ou gravá-la em um arquivo json. As chaves ausentes também podem ser adicionadas automaticamente aos arquivos de tradução fornecidos. ================================================ FILE: vuepress/pt/installation.md ================================================ # Instalação ## Nota de compatibilidade - Vue.js versões `2.0.0`+ ## Download direto / CDN O serviço [unpkg.com](https://unpkg.com) fornece links CDN com base em pacotes NPM. O link acima sempre apontará para a versão mais recente do NPM. Você pode usar uma versão ou tag específica usando um URL como este Ao conectar o vue-i18n após o Vue, o plug-in será instalado automaticamente: ```html ``` ## NPM ```bash npm install vue-i18n ``` ## Yarn ```bash yarn add vue-i18n ``` Ao usar o sistema de módulos, você deve definir explicitamente `vue-i18n` via `Vue.use()`: ```js import Vue from 'vue' import VueI18n from 'vue-i18n' Vue.use(VueI18n) ``` Você não precisa fazer isso ao usar tags de script globais `

{{ $t("message.hello") }}

``` ## JavaScript ```js // Se você usa um sistema de módulos (p. ex. via vue-cli) // Importe o Vue e VueI18n assim utilize-o Vue.use(VueI18n). // // import Vue from 'vue' // import VueI18n from 'vue-i18n' // // Vue.use(VueI18n) // Mensagens traduzidas de suas localidades const messages = { en: { message: { hello: 'Hello World' } }, pt: { message: { hello: 'Olá Mundo' } } } // Crie uma instância do VueI18n com opções const i18n = new VueI18n({ locale: 'pt', // Defina uma localidade messages, // Defina as mensagens }) // Crie uma instância Vue com a opção `i18n` new Vue({ i18n }).$mount('#app') // Pronto agora o aplicativo foi iniciado! ``` O resultado será o seguinte: ```html

Olá Mundo

``` ================================================ FILE: vuepress/ru/README.md ================================================ --- home: true heroImage: ./../vue-i18n-logo.png actionText: Введение → actionLink: introduction.md footer: MIT Licensed | Copyright © 2020 kazuya kawaguchi ---

🥇 Золотые спонсоры

Nuxt.js

🥈 Серебряные спонсоры

Редактор переводов BabelEdit для приложений (веб-приложений)

🥉 Бронзовые спонсоры

zenarchitects sendcloud

Простой

Позволяет легко добавить интернационализацию в приложение с помощью простого API

Функциональный

В дополнение к переводам, поддерживает плюрализацию, локализацию для чисел, дат ... и т.д.

Ориентированный на компоненты

Можно управлять сообщениями локализации в однофайловых компонентах

================================================ FILE: vuepress/ru/api/README.md ================================================ --- sidebar: auto --- # Справочник API ## Расширение прототипа Vue ### Опции конструктора Vue #### i18n * **Тип:** `I18nOptions` Опция локализации на основе компонентов. * **См. также:** Опции конструктора класса `VueI18n` ### Внедряемые методы #### $t * **Аргументы:** * `{Path} key`: обязательный * `{Locale} locale`: опционально * `{Array | Object} values`: опционально * **Возвращает:** `TranslateResult` Получение переведённого сообщения по ключу `key`. Сообщения локализации в компоненте имеют приоритет над глобальными сообщениями. Если сообщений локализации в компоненте нет, то локализация осуществляется с помощью глобальных сообщений локализации. Если указана `locale`, то используются сообщения локализации из `locale`. Если был указан `key` именованного формата / формата списков сообщений локализации, то необходимо указывать также `values`. Подробнее про значения `values` можно изучить в разделе [Формат сообщений локализации](../guide/formatting.md). :::danger Совет Обратите внимание, что в хуках жизненного цикла контекст должен быть экземпляром компонента (например в опции `data`, `const $t = this.$t.bind(this)`). ::: #### $tc * **Аргументы:** * `{Path} key`: обязательный * `{number} choice`: опционально, по умолчанию `1` * `{Locale} locale`: опционально * `{string | Array | Object} values`: опционально * **Возвращает:** `TranslateResult` Получение переведённого сообщения по ключу `key` с использованием плюрализации. Сообщения локализации компонента имеют приоритет над глобальными сообщениями. Если сообщений локализации в компоненте нет, то локализация осуществляется с помощью глобальных сообщений локализации. Если указана `locale`, то используются сообщения локализации из `locale`. Если указано строковое значение для `values`, то локализация выполняется для этого значения. Если указано значение Array или Object в `values`, то необходимо указывать с `values` из $t. Если вам не подходит реализация плюрализации по умолчанию, смотрите [pluralization rules в опциях конструктора](#pluralizationrules) и [пользовательскую плюрализацию](../guide/pluralization.md). :::danger Совет Обратите внимание, что в хуках жизненного цикла контекст должен быть экземпляром компонента (например в опции `data`, `const $tc = this.$tc.bind(this)`). ::: #### $te * **Аргументы:** * `{Path} key`: обязательный * `{Locale} locale`: опционально * **Возвращает:** `boolean` Проверяет существует ли перевод для ключа в сообщениях локализации. Если нет сообщений локализации в компоненте, то проверяет в глобальных сообщениях локализации. Если указана `locale`, то проверяется наличие в сообщениях локализации `locale`. :::danger Совет Обратите внимание, что в хуках жизненного цикла контекст должен быть экземпляром компонента (например в опции `data`, `const $te = this.$te.bind(this)`). ::: #### $d > :new: Добавлено в версии 7.0+ * **Аргументы:** * `{number | Date} value`: обязательный * `{Path | Object} key`: опционально * `{Locale | Object} locale`: опционально * **Возвращает:** `DateTimeFormatResult` Локализация даты из `value` по указанному формату даты из `key`. Формат указанный в `key` должен быть зарегистрирован в опции `dateTimeFormats` класса `VueI18n`, и зависит от опции `locale` конструктора `VueI18n`. Если указать аргумент `locale`, то он будет иметь приоритет над опцией `locale` конструктора `VueI18n`. Если формата даты для `key` нет в опции `dateTimeFormats`, то будет использован запасной формат, основываясь на опции `fallbackLocale` конструктора `VueI18n`. :::danger Совет Обратите внимание, что в хуках жизненного цикла контекст должен быть экземпляром компонента (например в опции `data`, `const $d = this.$d.bind(this)`). ::: #### $n > :new: Добавлено в версии 7.0+ * **Аргументы:** * `{number} value`: обязательный * `{Path | Object} format`: опционально * `{Locale} locale`: опционально * **Возвращает:** `NumberFormatResult` Локализация числа `value` с помощью формата чисел `format`. Числовой формат из `format` должен быть зарегистрирован в опции `numberFormats` класса `VueI18n`, и зависит от опции `locale` конструктора `VueI18n`. Если указать аргумент `locale`, то он будет иметь приоритет над опцией `locale` конструктора `VueI18n`. Если формат чисел для `format` не указан в опции `numberFormats`, будет использован запасной формат, основываясь на опции `fallbackLocale` конструктора `VueI18n`. Если второй аргумент `format` указан объектом, то в нём должны быть следующие свойства: * `key {Path}`: опционально, форматируемое число * `locale {Locale}`: опционально, локализация * `compactDisplay {string}`: опционально, опция форматирования чисел * `currency {string}`: опционально, опция форматирования чисел * `currencyDisplay {string}`: опционально, опция форматирования чисел * `currencySign {string}`: опционально, опция форматирования чисел * `localeMatcher {string}`: опционально, опция форматирования чисел * `notation {string}`: опционально, опция форматирования чисел * `numberingSystem {string}`: опционально, опция форматирования чисел * `signDisplay {string}`: опционально, опция форматирования чисел * `style {string}`: опционально, опция форматирования чисел * `unit {string}`: опционально, опция форматирования чисел * `unitDisplay {string}`: опционально, опция форматирования чисел * `useGrouping {boolean}`: опционально, опция форматирования чисел * `minimumIntegerDigits {string}`: опционально, опция форматирования чисел * `minimumFractionDigits {string}`: опционально, опция форматирования чисел * `maximumFractionDigits {string}`: опционально, опция форматирования чисел * `minimumSignificantDigits {string}`: опционально, опция форматирования чисел * `maximumSignificantDigits {string}`: опционально, опция форматирования чисел Любые указанные опции форматирования числа будут иметь приоритет над значениями `numberFormats` из конструктора `VueI18n`. :::danger Совет Обратите внимание, что в хуках жизненного цикла контекст должен быть экземпляром компонента (например в опции `data`, `const $n = this.$n.bind(this)`). ::: ### Внедряемые свойства #### $i18n * **Тип:** `I18n` * **Только для чтения** Получение экземпляра `VueI18n`, если был определён. Если в компоненте указана опция `i18n`, то получение экземпляра `VueI18n` компонента. В противном случае, получение корневого экземпляра `VueI18n`. ## Класс `VueI18n` Класс `VueI18n` реализует интерфейс `I18n` из [определений flowtype](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js) ### Статические свойства #### version * **Тип:** `string` Версия `vue-i18n`. #### availabilities > :new: Добавлено в версии 7.0+ * **Тип:** `IntlAvailability` Проверка доступности следующих возможностей интернационализации: * `{boolean} dateTimeFormat`: форматирование дат для локалей * `{boolean} numberFormat`: форматирование чисел для локалей Указанные выше возможности интернационализации зависят от [окружения браузера](http://kangax.github.io/compat-table/esintl/), в котором реализован ECMAScript Internationalization API (ECMA-402). ### Опции конструктора Можно указывать некоторые опции конструктора `I18nOptions`, основываясь на [определениях flowtype](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js) #### locale * **Тип:** `Locale` * **По умолчанию:** `'en-US'` Локаль используемая для локализации. Если локаль содержит территорию и диалект, то эта локаль явно определяет запасную локализацию. #### fallbackLocale * **Тип:** `FallbackLocale` * **По умолчанию:** `false` Запасная локаль для локализации. Подробнее в разделе [Запасная локализация](../guide/fallback.md). #### messages * **Тип:** `LocaleMessages` * **По умолчанию:** `{}` Сообщения локализации для локали. #### dateTimeFormats > :new: Добавлено в версии 7.0+ * **Тип:** `DateTimeFormats` * **По умолчанию:** `{}` Форматы дат для локализации. * **См. также:** тип `DateTimeFormats` в [определениях flowtype](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js) #### numberFormats > :new: Добавлено в версии 7.0+ * **Тип:** `NumberFormats` * **По умолчанию:** `{}` Форматы чисел для локализации. * **См. также:** тип `NumberFormats` в [определениях flowtype](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js) #### availableLocales > :new: Добавлено в версии 8.9.0+ * **Тип:** `Locale[]` * **По умолчанию:** `[]` * **Примеры:** `["en", "ru"]` Список доступных локалей в `messages` в лексическом порядке. #### formatter * **Тип:** `Formatter` * **По умолчанию:** Встроенный formatter Метод форматирования, реализующий интерфейс `Formatter`. #### modifiers > :new: Добавлено в версии 8.15.0+ * **Тип:** `Modifiers` * **По умолчанию:** модификаторы `lower` и `upper` Функции модификаторов для связанных сообщений #### missing * **Тип:** `MissingHandler` * **По умолчанию:** `null` Обработчик для отсутствующих сообщений локализации. Обработчик будет вызван с локалью, ключом сообщения локализации и значениями values. Если указан этот обработчик и произойдёт попытка доступа к отсутствующему сообщению локализации, то предупреждения в консоли не будет. #### fallbackRoot * **Тип:** `Boolean` * **По умолчанию:** `true` При использовании локализации в компонентах определяет обращаться ли к локализации корневого уровня (глобальной) при неудаче. При значении `false` будет выбрасываться предупреждение и возвращаться ключ. #### fallbackRootWithEmptyString > :new: Добавлено в версии 8.26+ * **Тип:** `Boolean` * **По умолчанию:** `true` В локализации компонента укажите, следует ли вернуться к локализации корневого уровня (глобальной), если локальное сообщение представляет собой пустую строку. Обратите внимание, что поведение по умолчанию в vue-i18n 9.x заключается в том, чтобы не возвращаться к корневому каталогу для локального сообщения, которое является пустой строкой. Если `false`, пустое локальное сообщение не будет возвращено в корень и будет храниться как пустая строка. #### sync * **Тип:** `Boolean` * **По умолчанию:** `true` Синхронизировать ли локализацию корневого уровня с локализацией компонента. При значении `false`, независимо от локали определённой на корневом уровне, будет использоваться локаль установленная в компоненте. #### silentTranslationWarn > 6.1+, :up: 8.13 * **Тип:** `Boolean | RegExp` * **По умолчанию:** `false` Отключение предупреждений, отображаемых при неудаче локализации. При значении `true`, отключаются предупреждения об ошибках локализации. Если использовать регулярное выражение, то можно отключать предупреждения об ошибках, которые будут соответствовать `key` (например, `$t`). #### silentFallbackWarn > :new: Добавлено в версии 8.8+, :up: 8.13 * **Тип:** `Boolean | RegExp` * **По умолчанию:** `false` Отключение предупреждений при использовании запасной локали из `fallbackLocale` или `root`. При значении `true` предупреждения будут генерироваться только если недоступна локализация вообще, а не для случаев использования запасной локали. При использовании регулярного выражения можно отключать предупреждения которые будут соответствовать `key` (например, `$t`). #### pluralizationRules > 8.5+ * **Тип:** `PluralizationRules` * **По умолчанию:** `{}` Набор правил для плюрализации в следующем формате: ```js { // Ключ - локаль для которой будет применяться правило. // Value - функция для получения индекса варианта плюрализации от текущего числа и заданного количества вариантов. (См. функцию getChoiceIndex) 'pt': function(choice, choiceIndex) => Number/* index of the plural word */; 'ru': function(choice, choiceIndex) => Number/* index of the plural word */; 'en': function(choice, choiceIndex) => Number/* index of the plural word */; 'jp': function(choice, choiceIndex) => Number/* index of the plural word */; } ``` #### preserveDirectiveContent > Добавлено в версии 8.7+ * **Тип:** `Boolean` * **По умолчанию:** `false` Определяет должен ли элемент директивы `v-t` сохранять `textContent` после того как директива будет снята с элемента. #### warnHtmlInMessage > Добавлено в версии 8.11+ * **Тип:** `WarnHtmlInMessageLevel` * **По умолчанию:** `off` Разрешить ли использование HTML-форматирования в сообщениях локализации. См. также свойство `warnHtmlInMessage`. :::danger Внимание! Со следующей мажорной версии значение по умолчанию `warnHtmlInMessage` будет `warn`. ::: #### sharedMessages > Добавлено в версии 8.12+ * **Тип:** `LocaleMessages` * **По умолчанию:** `undefined` Общие сообщения локализации при локализации в компонентах. Подробнее в разделе [Локализация на основе компонентов](../guide/component.md#локаnизация-на-основе-компонентов). #### postTranslation > Добавлено в версии 8.16+ * **Тип:** `PostTranslationHandler` * **По умолчанию:** `null` Пост-обработчик локализации. Выполняется после вызова `$t`, `t`, `$tc` и `tc`. Может пригодиться при необходимости дополнительно обработать итоговый текст перевода, например избавиться от висящих пробелов. #### componentInstanceCreatedListener > Добавлено в версии 8.18+ * **Тип:** `ComponentInstanceCreatedListener` * **По умолчанию:** `null` Обработчик получения уведомления о создании локального экземпляра компонента. Вызывается с новым и старым (корневыми) экземплярами VueI18n. Обработчик может потребоваться при расширении корневого экземпляра VueI18n и необходимости ожидания для применения этих расширений к локальному экземпляру компонента. #### escapeParameterHtml > Добавлено в версии 8.22+ * **Тип:** `Boolean` * **По умолчанию:** `false` Если `escapeParameterHtml` установлен в значение `true`, то параметры интерполяции будут экранированы перед переводом сообщения. Это полезно, когда результат перевода используется в `v-html` и текст для перевода содержит HTML-разметку (например, `` вокруг предоставленного пользователем значения). Этот шаблон в основном предназначен для случаев, когда передаются предварительно скомпилированные текстовые строки в компоненты UI. Процесс экранирования включает в себя замену следующих символов на соответствующие HTML-сущности: `<`, `>`, `"`, `'`. Установка `escapeParameterHtml` в значение `true` не должна нарушать существующую функциональность, а предоставит защиту от векторов атаки типа XSS. ### Свойства #### locale * **Тип:** `Locale` * **Чтение/Запись** Локаль используемая для локализации. Если локаль содержит территорию и диалект, то эта локаль неявно указывает на запасные варианты. #### fallbackLocale * **Тип:** `FallbackLocale` * **Чтение/Запись** Локаль используемая для запасной локализации. Способы определения и переключения на запасную локализацию можно изучить в разделе [Запасная локализация](../guide/fallback.md). #### messages * **Тип:** `LocaleMessages` * **Только для чтения** Переведённые сообщения используемые для локализации. #### dateTimeFormats > :new: Добавлено в версии 7.0+ * **Тип:** `DateTimeFormats` * **Только для чтения** Форматы форматирования дат для локализации. #### numberFormats > :new: Добавлено в версии 7.0+ * **Тип:** `NumberFormats` * **Только для чтения** Форматы форматирования чисел для локализации. #### missing * **Тип:** `MissingHandler` * **Чтение/Запись** Обработчик для отсутствующих ключей локализаций. #### formatter * **Тип:** `Formatter` * **Чтение/Запись** Метод форматирования, который реализует интерфейс `Formatter`. #### silentTranslationWarn > 6.1+, :up: 8.13 * **Тип:** `Boolean | RegExp` * **Чтение/Запись** Отключение предупреждений выводимых при ошибке локализации. #### silentFallbackWarn > :new: Добавлено в версии 8.8+, :up: 8.13 * **Тип:** `Boolean | RegExp` * **Чтение/Запись** Отключение предупреждений выводимых при ошибке использования запасной локализации. #### pluralizationRules > 8.5+ * **Тип:** `PluralizationRules` * **Чтение/Запись** Набор зависимых от локали правил плюрализации. #### preserveDirectiveContent > Добавлено в версии 8.7+ * **Тип:** `Boolean` * **Чтение/Запись** Должен ли элемент директивы `v-t` сохранять `textContent` после того как директива снята с элемента. #### warnHtmlInMessage > Добавлено в версии 8.11+ * **Тип:** `WarnHtmlInMessageLevel` * **Чтение/Запись** Разрешить ли использование HTML-форматирования в сообщениях локализации. При установке `warn` или `error` проверяются сообщения локализации экземпляра VueI18n. При установке `warn` выводятся предупреждения в консоль. При установке `error` генерируется Error. В качестве значения по умолчанию в экземпляре VueI18n установлено `off`. #### postTranslation > Добавлено в версии 8.16+ * **Тип:** `PostTranslationHandler` * **Чтение/Запись** Обработчик для пост-обработки перевода. ### Методы #### getChoiceIndex * **Аргументы:** * `{number} choice` * `{number} choicesLength` * **Возвращает:** `finalChoice {number}` Получение индекса для плюрализации текущего числа и заданного количества вариантов. Реализация может быть переопределена через изменение прототипа: ```js VueI18n.prototype.getChoiceIndex = /* пользовательская реализация */ ``` Однако в большинстве случаев достаточно передать нужную функцию в [pluralizationRules опцию конструктора](#pluralizationrules). #### getLocaleMessage( locale ) * **Аргументы:** * `{Locale} locale` * **Возвращает:** `LocaleMessageObject` Получение сообщений локализации для локали. #### setLocaleMessage( locale, message ) * **Аргументы:** * `{Locale} locale` * `{LocaleMessageObject} message` Установка сообщений локализации для локали. :::tip ПРИМЕЧАНИЕ > Добавлено в версии 8.11+ При использовании `warn` или `error` в свойстве `warnHtmlInMessage`, при выполнении этого метода будет проверено используется ли HTML-форматирование для сообщения локализации. ::: #### mergeLocaleMessage( locale, message ) > 6.1+ * **Аргументы:** * `{Locale} locale` * `{LocaleMessageObject} message` Объединение указанных сообщений локализации с сообщениями локализации локали. :::tip ПРИМЕЧАНИЕ > Добавлено в версии 8.11+ При использовании `warn` или `error` в свойстве `warnHtmlInMessage`, при выполнении этого метода будет проверено используется ли HTML-форматирование для сообщения локализации. ::: #### t( key, [locale], [values] ) * **Аргументы:** * `{Path} key`: обязательный * `{Locale} locale`: опционально * `{Array | Object} values`: опционально * **Возвращает:** : `TranslateResult` Аналогично функции возвращаемой методом `$t`. Подробнее см. [$t](#t). #### tc( key, [choice], [values] ) * **Аргументы:** * `{Path} key`: обязательный * `{number} choice`: опционально, по умолчанию `1` * `{string | Array | Object} values`: опционально * **Возвращает:** `TranslateResult` Аналогично функции возвращаемой методом `$tc`. Подробнее см. [$tc](#tc). #### te( key, [locale] ) * **Аргументы:** * `{string} key`: обязательный * `{Locale} locale`: опционально * **Возвращает:** `boolean` Проверяет существует ли указанный ключ в глобальных сообщениях локализации. Если указать `locale`, проверка будет осуществляться в сообщениях локализации `locale`. #### getDateTimeFormat ( locale ) > :new: Добавлено в версии 7.0+ * **Аргументы:** * `{Locale} locale` * **Возвращает:** `DateTimeFormat` Получение форматов форматирования дат локализации. #### setDateTimeFormat ( locale, format ) > :new: Добавлено в версии 7.0+ * **Аргументы:** * `{Locale} locale` * `{DateTimeFormat} format` Установка форматов форматирования дат для локализации. #### mergeDateTimeFormat ( locale, format ) > :new: Добавлено в версии 7.0+ * **Аргументы:** * `{Locale} locale` * `{DateTimeFormat} format` Объединение указанных форматов форматирования дат с форматами локализации. #### d( value, [key], [locale] ) > :new: Добавлено в версии 7.0+ * **Аргументы:** * `{number | Date} value`: обязательный * `{Path | Object} key`: опционально * `{Locale | Object} locale`: опционально * **Возвращает:** `DateTimeFormatResult` Аналогично функции возвращаемой методом `$d`. Подробнее см. [$d](#d). #### getNumberFormat ( locale ) > :new: Добавлено в версии 7.0+ * **Аргументы:** * `{Locale} locale` * **Возвращает:** `NumberFormat` Получение форматов форматирования чисел для локализации. #### setNumberFormat ( locale, format ) > :new: Добавлено в версии 7.0+ * **Аргументы:** * `{Locale} locale` * `{NumberFormat} format` Установка форматов форматирования чисел для локализации. #### mergeNumberFormat ( locale, format ) > :new: Добавлено в версии 7.0+ * **Аргументы:** * `{Locale} locale` * `{NumberFormat} format` Объединение указанных форматов форматирования чисел с форматами локализации. #### n( value, [format], [locale] ) > :new: Добавлено в версии 7.0+ * **Аргументы:** * `{number} value`: обязательный * `{Path | Object} format`: опционально * `{Locale} locale`: опционально * **Возвращает:** `NumberFormatResult` Аналогично функции возвращаемой методом `$n`. Подробнее см. [$n](#n). ## Директивы > :new: Добавлено в версии 7.3+ ### v-t * **Ожидает:** `string | Object` * **Модификаторы:** * `.preserve`: (8.7.0+) сохраняет `textContent` элемента при снятии директивы с элемента. * **Подробности:** Обновление `textContent` элемента, который был переведён с помощью сообщений локализации. Можно использовать строковый или объектный синтаксис. Строковый синтаксис может быть задан в качестве пути к сообщению локализации. При использовании объектного синтаксиса необходимо указать следующие свойства: * `path`: обязательный, ключ сообщения локализации * `locale`: опционально, локализация * `args`: опционально, для списка или именованного форматирования :::tip ПРИМЕЧАНИЕ По умолчанию значение `textContent` элемента удаляется при снятии директивы `v-t`. Это может быть нежелательной ситуацией например при [анимировании списков](https://ru.vuejs.org/v2/guide/transitions.html). Для сохранения данных `textContent` после снятия директивы следует использовать модификатор `.preserve` или глобальную опцию [`preserveDirectiveContent`](#preservedirectivecontent). ::: * **Примеры:** ```html

``` * **См. также:** [Пользовательская директива для локализации](../guide/directive.md) ## Компоненты ### Функциональный компонент i18n > :new: Добавлено в версии 7.0+ #### Входные параметры: * `path {Path}`: обязательный, путь к сообщению локализации * `locale {Locale}`: опционально, локализация * `tag {string | boolean | Object}`: опционально, по умолчанию `'span'` * `places {Array | Object}`: опционально (7.2+) :::danger Внимание! Со следующей мажорной версии опция `places` будет удалена. Используйте синтаксис слотов. ::: #### Использование: ```html ``` ```js const messages = { en: { tos: 'Term of Service', term: 'I accept xxx {0}.' }, ru: { tos: 'Условия обслуживания', term: 'Я соглашаюсь с xxx {0}.' } } const i18n = new VueI18n({ locale: 'en', messages }) new Vue({ i18n, data: { url: '/term' } }).$mount('#app') ``` #### См. также: [Интерполяция компонента](../guide/interpolation.md) ### Функциональный компонент i18n-n > :new: Добавлено в версии 8.10+ #### Входные параметры: * `value {number}`: обязательный, число для форматирования * `format {string | NumberFormatOptions}`: опционально, форматируемое число или объект с указанными опциями форматирования * `locale {Locale}`: опционально, локализация * `tag {string | boolean | Object}`: опционально, по умолчанию `'span'` #### Использование: ```html
{{ slotProps.currency }}
``` ```js var numberFormats = { 'en-US': { currency: { style: 'currency', currency: 'USD' } }, 'ru-RU': { currency: { style: 'currency', currency: 'RUB' } } } const i18n = new VueI18n({ locale: 'en-US', numberFormats }) new Vue({ i18n, data: { money: 10234 } }).$mount('#app') ``` #### Слоты с ограниченной областью видимости Функциональный компонент `` может принимать различные слоты с ограниченной областью видимости. Список поддерживаемых имён слотов основан на [выходных типах `Intl.NumberFormat.formatToParts()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat/formatToParts): * `currency` * `decimal` * `fraction` * `group` * `infinity` * `integer` * `literal` * `minusSign` * `nan` * `plusSign` * `percentSign` Каждый из слотов предоставляет доступ к значениям трёх параметров: * `[slotName] {FormattedNumberPartType}`: параметр с тем же именем, что и имя слота (например, `integer`) * `index {Number}`: индекс конкретной части в массиве частей числа * `parts {Array}`: массив со всеми отформатированными частями числа #### См. также: [Локализация чисел](../guide/number.md#custom-formatting) ## Специальные атрибуты ### place > :new: Добавлено в версии 7.2+ #### Ожидает: `{number | string}` Используется при интерполяции компонента для указания индекса при форматировании списком или ключа при именованном форматировании. Подробнее об использовании в разделе по ссылке ниже. #### См. также: [Интерполяция компонента](../guide/interpolation.md) ================================================ FILE: vuepress/ru/guide/component.md ================================================ # Локализация на основе компонентов В основном данные для локализации (например, `locale`,`messages`, и т.д.) задаются опциями конструктора экземпляра `VueI18n` и устанавливаются через свойство `i18n` в корневой экземпляр Vue. Поэтому можно глобально выполнять переводы, используя методы `$t` или `$tc` в корневом экземпляре Vue и любом из компонентов в нём. Но также возможно указывать данные для локализации в каждом компоненте в отдельности, что может быть удобнее благодаря компонентно-ориентированному дизайну. Пример локализации на основе компонентов: ```js // Установка локализации в корневой экземпляр Vue const i18n = new VueI18n({ locale: 'ru', messages: { en: { message: { hello: 'hello world', greeting: 'good morning' } }, ru: { message: { hello: 'привет мир', greeting: 'доброе утро' } } } }) // Определение компонента const Component1 = { template: `

Component1 locale messages: {{ $t("message.hello") }}

Fallback global locale messages: {{ $t("message.greeting") }}

`, i18n: { // опция `i18n`, определение данных локализации для компонента messages: { en: { message: { hello: 'hello component1' } }, ru: { message: { hello: 'привет component1' } } } } } new Vue({ i18n, components: { Component1 } }).$mount('#app') ``` Шаблон: ```html

{{ $t("message.hello") }}

``` Результат: ```html

привет мир

Component1 locale messages: привет component1

Fallback global locale messages: доброе утро

``` Если компонент не имеет собственного сообщения для локализации, то в качестве запасного выхода он обратится к глобальным данным для локализации. Компонент использует локаль, установленную в корневом экземпляре (в примере выше установлена: `locale: 'ru'`). Обратите внимание, по умолчанию при обращении к данным корневой локализации будут генерироваться предупреждения в консоли: ``` [vue-i18n] Value of key 'message.greeting' is not a string! [vue-i18n] Fall back to translate the keypath 'message.greeting' with root locale. ``` Чтобы скрыть эти предупреждения (оставив те, что предупреждают о полном отсутствии перевода для данного ключа) установите опцию `silentFallbackWarn: true` при инициализации экземпляра `VueI18n`. Если необходимо осуществлять перевод, основываясь на локали компонента, то это можно сделать с помощью опции `sync: false` и `locale` в настройках `i18n`. ## Общие сообщения локализации для компонентов Иногда может потребоваться импортировать сообщения локализации в определённых компонентах, чтобы не обращаться к глобальным сообщениям локализации (например, общие сообщения для определённых функций компонентов). Для этого можно использовать опцию `sharedMessages` в свойстве `i18n` компонента. Пример использования общих сообщений локализации: ```js export default { en: { buttons: { save: 'Save' // ... } }, ru: { buttons: { save: 'Сохранить' // ... } } } ``` Компонент: ```js import commonMessage from './locales/common' // импорт общих сообщений локализации export default { name: 'ServiceModal', template: ` `, i18n: { messages: { ... }, sharedMessages: commonMessages } } ``` Если указаны опции `sharedMessages` и `messages`, то их сообщения будут объединены в сообщения локализации в экземпляре VueI18n этого компонента. ## Локализация в функциональных компонентах При использовании функционального компонента все данные (включая `props`, `children`, `slots`, `parent`, и т.д.) передаются через `context`, в котором содержатся все эти атрибуты. Кроме того отсутствует возможность использовать `this`, поэтому при использовании vue-i18n с функциональными компонентами следует обращаться к `$t` как к `parent.$t`, например так: ```html ... ... ``` ================================================ FILE: vuepress/ru/guide/datetime.md ================================================ # Локализация дат :::tip Поддержка с версии :new: 7.0+ ::: Можно выполнять локализацию дат по соответствующему формату. Пример формата для дат: ```js const dateTimeFormats = { 'en-US': { short: { year: 'numeric', month: 'short', day: 'numeric' }, long: { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long', hour: 'numeric', minute: 'numeric' } }, 'pt-BR': { short: { year: 'numeric', month: 'short', day: 'numeric' }, long: { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long', hour: 'numeric', minute: 'numeric', hour12: true } } } ``` Как видно выше, можно определять именованный формат даты (например, `short`, `long` и т.д.) используя [опции ECMA-402 Intl.DateTimeFormat](http://www.ecma-international.org/ecma-402/2.0/#sec-intl-datetimeformat-constructor) После этого, для возможности использования данного формата в сообщениях локализации, необходимо задать опцию `dateTimeFormats` в конструкторе `VueI18n`: ```js const i18n = new VueI18n({ dateTimeFormats }) new Vue({ i18n }).$mount('#app') ``` Шаблон: ```html

{{ $d(new Date(), 'short') }}

{{ $d(new Date(), 'long', 'pt-BR') }}

``` Результат: ```html

Jan 18, 2021

domingo, 18 de janeiro de 2021 5:47 AM

``` ================================================ FILE: vuepress/ru/guide/directive.md ================================================ # Пользовательская директива :::tip Поддержка с версии :new: 7.3+ ::: Переводы можно осуществлять не только используя пользовательскую директиву `v-t`, но и с помощью метода `$t`. ## Строковый синтаксис Можно передавать ключ сообщения локализации строкой. JavaScript: ```js new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { hello: 'hi there!' }, ru: { hello: 'привет всем!' } } }), data: { path: 'hello' } }).$mount('#string-syntax') ``` Шаблон: ```html

``` Результат: ```html

привет всем!

привет всем!

``` ## Объектный синтаксис Можно использовать объектный синтаксис. JavaScript: ```js new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { hello: 'hi {name}!' }, ru: { hello: 'привет {name}!' } } }), computed: { nickName() { return 'kazupon' } }, data: { path: 'hello' } }).$mount('#object-syntax') ``` Шаблон: ```html

``` Результат: ```html

привет、kazupon!

hi kazupon!

``` ## Использование с transition :::tip Поддержка с версии :new: 8.7+ ::: При использовании директивы `v-t` на элементе внутри [компонента ``](https://ru.vuejs.org/v2/api/#transition), можно заметить как переведённое сообщение исчезает во время анимации перехода. Это поведение связано с реализацией самого компонента `` — все директивы в исчезающем элементе внутри компонента `` должны быть уничтожены **до начала анимации**. Это может привести к мерцанию содержимого на коротких анимациях, но наиболее заметно при длинных анимациях переходов. Чтобы сохранить содержимое директивы во время анимации перехода, необходимо добавить [модификатор `.preserve`](../api/#v-t) при определении директивы `v-t`. Javascript: ```js new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { preserve: 'with preserve' } } }), data: { toggle: true } }).$mount('#in-transitions') ``` Шаблон: ```html
``` Также можно глобально установить настройку `preserveDirectiveContent` в экземпляре `VueI18n`, что повлияет на все директивы `v-t` без добавления модификатора к ним. JavaScript: ```js new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { preserve: 'with preserve' } }, preserveDirectiveContent: true }), data: { toggle: true } }).$mount('#in-transitions') ``` Шаблон: ```html
``` Подробнее о примерах выше можно изучить [здесь](https://github.com/kazupon/vue-i18n/tree/dev/examples/directive) ## `$t` или `v-t` ### `$t` `$t` — это метод, добавленный в экземпляр Vue. У него следующие плюсы и минусы: #### Плюсы Предоставляет **гибкость** в использовании синтаксиса фигурных скобок `{{}}` в шаблонах, а также применять в вычисляемых свойствах и методах экземпляра Vue. #### Минусы `$t` выполняется **каждый раз** когда происходит перерисовка, поэтому у него есть расходы на осуществление перевода. ### `v-t` `v-t` — пользовательская директива. У неё следующие плюсы и минусы: #### Плюсы `v-t` имеет **лучшую производительность** в сравнении с методом `$t`, благодаря кэшу в пользовательской директиве после выполнения перевода. Также можно реализовать предварительный перевод с помощью модуля для компилятора Vue, который предоставляет плагин [`vue-i18n-extensions`](https://github.com/kazupon/vue-i18n-extensions). Таким образом, можно достичь **большей оптимизации производительности**. #### Минусы `v-t` нельзя использовать также гибко, как `$t`, и это добавляет **сложности**. Перевод с помощью `v-t` вставляется в `textContent` элемента. Также, при использовании рендеринга на стороне сервера необходимо установить [пользовательскую директиву](https://github.com/kazupon/vue-i18n-extensions#directive-v-t-custom-directive-for-server-side) через опцию `directives` функции `createRenderer`. ================================================ FILE: vuepress/ru/guide/fallback.md ================================================ # Запасная локализация *Вкратце: указывайте `fallbackLocale: ''` для определения языка, который будет использоваться, если нет перевода в выбранной локализации.* ## Неявное определение запасных локализаций при использовании локалей Если `locale` содержит территорию и опционально диалект, то неявно будут определены автоматически запасные локали. Например для `de-DE-bavarian` в качестве запасных будут считаться следующие: 1. `de-DE-bavarian` 2. `de-DE` 3. `de` Для отключения автоматического определения запасных локалей укажите символ `!`, например `de-DE!` ## Явное определение одной локали запасной локализации Иногда не все ключи сообщений переведены на другие языки. В примере ниже, сообщение для ключа `hello` доступно в английской локали, но отсутствует в русской: ```js const messages = { en: { hello: 'Hello, world!' }, ru: { // упс, не все переведено } } ``` Если хочется использовать сообщения локализации из `en`, когда перевод отсутствует в нужной локализации, то следует указать опцию `fallbackLocale` в конструкторе VueI18n: ```js const i18n = new VueI18n({ locale: 'ru', fallbackLocale: 'en', messages }) ``` Шаблон: ```html

{{ $t('hello') }}

``` Результат: ```html

Hello, world!

``` По умолчанию, если использовались сообщения запасной локализации из `fallbackLocale`, то в консоли будут выведены соответствующие предупреждения: ``` [vue-i18n] Value of key 'hello' is not a string! [vue-i18n] Fall back to translate the keypath 'hello' with 'en' locale. ``` Чтобы скрыть такие предупреждения (оставив при этом те, в случаях полного отсутствия переводов для данного ключа) установите `silentFallbackWarn: true` при инициализации экземпляра `VueI18n`. ## Явное определение запасной локали с помощью массива Можно указать более одной запасной локализации с помощью массива. Например: ```js fallbackLocale: ['ru', 'en'], ``` ## Явное определение запасной локали с помощью объекта Более сложный алгоритм принятия решений для определения запасной локали можно реализовать с помощью карты принятия решений в виде объекта. Например для подобного объекта: ```js fallbackLocale: { /* 1 */ 'de-CH': ['fr', 'it'], /* 2 */ 'zh-Hant': ['zh-Hans'], /* 3 */ 'es-CL': ['es-AR'], /* 4 */ 'es': ['en-GB'], /* 5 */ 'pt': ['es-AR'], /* 6 */ 'default': ['en', 'ru'] }, ``` Будут следующие цепочки выбора запасной локали: | Локаль | Цепочка выбора | | ----------- | ----------------------------------------- | | `'de-CH'` | de-CH > fr > it > en > ru | | `'de'` | de > en > ru | | `'zh-Hant'` | zh-Hant > zh-Hans > zh > en > ru | | `'es-SP'` | es-SP > es > en-GB > en > ru | | `'es-SP!'` | es-SP > en > ru | | `'fr'` | fr > en > ru | | `'pt-BR'` | pt-BR > pt > es-AR > es > en-GB > en > ru | | `'es-CL'` | es-CL > es-AR > es > en-GB > en > ru | ## Резервная интерполяция _Вкратце: установите `formatFallbackMessages: true` чтобы выполнять интерполяции шаблона по ключам перевода, когда в выбранном языке отсутствует данный ключ для перевода._ Так как ключи переводов являются строками, то можно использовать само сообщение в качестве ключа (для определённого языка). Например: ```js const messages = { ru: { 'Hello, world!': 'Привет мир!' } } ``` Это может быть полезным, потому что не нужно будет указывать перевод для строки "Hello, world!" в английской локализации. Фактически, можно указывать даже параметры в ключе. Вместе с `formatFallbackMessages: true` это позволит опустить создание шаблонов для «базового» языка; потому что его ключи _уже находятся_ в шаблоне. ```js const messages = { ru: { 'Hello {name}': 'Здравствуйте {name}' } } const i18n = new VueI18n({ locale: 'ru', fallbackLocale: 'en', formatFallbackMessages: true, messages }) ``` Шаблон: ```html

{{ $t('Hello {name}', { name: 'John' }}) }}

{{ $t('The weather today is {condition}!', { condition: 'sunny' }) }}

``` Результат: ```html

Здравствуйте, John

The weather today is sunny!

``` ================================================ FILE: vuepress/ru/guide/formatting.md ================================================ # Формат сообщений локализации ## Именованный формат Сообщения локализации: ```js const messages = { en: { message: { hello: '{msg} world' } }, ru: { message: { hello: '{msg} мир' } } } ``` Шаблон: ```html

{{ $t('message.hello', { msg: 'привет' }) }}

``` Результат: ```html

привет мир

``` ## Формат списков Сообщения локализации: ```js const messages = { en: { message: { hello: '{0} world' } }, ru: { message: { hello: '{0} мир' } } } ``` Шаблон: ```html

{{ $t('message.hello', ['привееет']) }}

``` Результат: ```html

привееет мир

``` Форматирование списков также принимает объекты, соответствующие по структуре массиву: ```html

{{ $t('message.hello', {'0': 'привееет'}) }}

``` Результат: ```html

привееет мир

``` ## HTML формат :::warning Обратите внимание :warning: Динамическая локализация произвольного HTML на вебсайте очень опасна, потому что легко может привести к XSS-уязвимостям. Используйте HTML-интерполяцию только для доверенного контента и никогда для пользовательского. Рекомендуем в таких случаях использовать возможности [интерполяции компонента](interpolation.md). ::: :::warning Обратите внимание > :new: Добавлено в версии 8.11+ Можно управлять использованием HTML форматирования. Для подробностей см. опцию конструктора `warnHtmlInMessage` и свойства API. ::: Иногда требуется отобразить сообщение локализации HTML-кодом, а не статической строкой. ```js const messages = { en: { message: { hello: 'hello
world' } }, ru: { message: { hello: 'привет
мир' } } } ``` Шаблон: ```html

``` Результат (вместо отформатированного сообщения) ```html

привет мир

``` ## Формат ruby on rails i18n Сообщения локализации: ```js const messages = { en: { message: { hello: '%{msg} world' } }, ru: { message: { hello: '%{msg} мир' } } } ``` Шаблон: ```html

{{ $t('message.hello', { msg: 'привет' }) }}

``` Результат: ```html

привет мир

``` ## Пользовательский формат Иногда может потребоваться реализовать локализацию для собственного формата сообщений (например, использовать [синтаксиса сообщений ICU](http://userguide.icu-project.org/formatparse/messages)). Реализовать это можно с помощью специального пользовательского метода форматирования, который должен реализовать [интерфейс Formatter](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js#L145-L147). Пример пользовательского метода форматирования с синтаксисом класса ES2015: ```js // Реализация пользовательского Formatter class CustomFormatter { constructor(options) { // ... } // // interpolate // // @param {string} message // строка или список или именованный формат // напр. // - именованный формат: 'Hi {name}' // - формат списка: 'Hi {0}' // // @param {Object | Array} values // значения интерполяции `message`. // переданные значения с `$t`, `$tc` и функциональным компонентом `i18n`. // напр. // - $t('hello', { name: 'Alex' }) -> переданные значения: Object `{ name: 'Alex' }` // - $t('hello', ['Alex']) -> переданные значения: Array `['Alex']` // - функциональный компонент `i18n` (интерполяция в компоненте) // //

Alex

//

how are you?

//
// -> переданные значения: Array (included VNode): // `[VNode{ tag: 'p', text: 'Alex', ...}, VNode{ tag: 'p', text: 'how are you?', ...}]` // // @return {Array} // интерполированные значения. Они необходимы чтобы вернуть следующее: // - массив строк, когда используется `$t` или `$tc`. // - массив, включая объект VNode, когда используется функциональный компонент `i18n`. // interpolate(message, values) { // реализация логики интерполяции // ... // возвращаем интерполированный массив return ['resolved message string'] } } // Регистрация через опцию `formatter` const i18n = new VueI18n({ locale: 'en-US', formatter: new CustomFormatter(/* опции конструктора */), messages: { 'en-US': { // ... }, 'ru-RU': { // ... } // ... } }) // Запускаем приложение! new Vue({ i18n }).$mount('#app') ``` Также посмотрите [официальный пример пользовательского метода форматирования](https://github.com/kazupon/vue-i18n/tree/dev/examples/formatting/custom). ================================================ FILE: vuepress/ru/guide/hot-reload.md ================================================ # Горячая перезагрузка переводов С помощью функции Webpack для [горячей перезагрузки модулей (HMR)](https://webpack.js.org/concepts/hot-module-replacement/) можно отслеживать изменения в файлах локализации и осуществлять их горячую перезагрузку в приложении. ## Простой пример Для статичного набора локалей, можно явно указать горячую перезагрузку этих переводов: ```js import Vue from "vue" import VueI18n from "vue-i18n" import en from './en' import ru from './ru' const messages = { en, ru } // Экземпляр VueI18n const i18n = new VueI18n({ locale: 'en', messages }) // Запускаем приложение const app = new Vue({ i18n // ... }).$mount('#app') // Добавляем горячую перезагрузку сообщений локализации if (module.hot) { module.hot.accept(['./en', './ru'], function() { i18n.setLocaleMessage('en', require('./en').default) i18n.setLocaleMessage('ru', require('./ru').default) // Или добавляем горячую перезагрузку через свойство $i18n // app.$i18n.setLocaleMessage('en', require('./en').default) // app.$i18n.setLocaleMessage('ru', require('./ru').default) }) } ``` ## Продвинутый пример Если требуется поддержка изменяющегося набор переводов, можно реализовать горячую перезагрузку для всех локалей динамически через `require.context`: ```js import Vue from "vue"; import VueI18n from "vue-i18n"; Vue.use(VueI18n); // Загрузка всех локалей и сохранение контекста function loadMessages() { const context = require.context("./locales", true, /[a-z0-9-_]+\.json$/i); const messages = context .keys() .map((key) => ({ key, locale: key.match(/[a-z0-9-_]+/i)[0] })) .reduce( (messages, { key, locale }) => ({ ...messages, [locale]: context(key), }), {} ); return { context, messages }; } const { context, messages } = loadMessages(); // Экземпляр VueI18n const i18n = new VueI18n({ locale: "en", messages, }); // Запускаем приложение const app = new Vue({ i18n, // ... }).$mount('#app'); // Добавляем горячую перезагрузку сообщений локализации if (module.hot) { module.hot.accept(context.id, () => { const { messages: newMessages } = loadMessages(); Object.keys(newMessages) .filter((locale) => messages[locale] !== newMessages[locale]) .forEach((locale) => { messages[locale] = newMessages[locale]; i18n.setLocaleMessage(locale, messages[locale]); }); }); } ``` ================================================ FILE: vuepress/ru/guide/interpolation.md ================================================ # Интерполяция компонента ## Базовое использование :::tip Поддержка с версии :new: 7.0+ ::: Иногда требуется перевести сообщения в которых есть HTML теги или компоненты. Например: ```html

I accept xxx Terms of Service Agreement

``` Для такого сообщения, если хотим использовать `$t`, то, вероятно, попробуем достичь скомпоновав из следующих сообщений локализации: ```js const messages = { en: { term1: "I Accept xxx's", term2: 'Terms of Service Agreement' } } ``` И в итоге шаблон станет выглядеть так: ```html

{{ $t('term1') }}{{ $t('term2') }}

``` Результат: ```html

I accept xxx Terms of Service Agreement

``` Это выглядит очень громоздко, но если перенести тег `` в сообщение локализации, то добавится вероятность XSS-уязвимости из-за применения `v-html="$t('term')"`. Этого можно избежать воспользовавшись функциональным компонентом `i18n`. Например: ```html ``` ```js const messages = { en: { tos: 'Term of Service', term: 'I accept xxx {0}.' }, ru: { tos: 'Условия обслуживания', term: 'Я соглашаюсь с xxx {0}.' } } const i18n = new VueI18n({ locale: 'en', messages }) new Vue({ i18n, data: { url: '/term' } }).$mount('#app') ``` Результат: ```html
``` Подробнее о примере можно изучить [здесь](https://github.com/kazupon/vue-i18n/tree/dev/examples/interpolation/places) Потомки функционального компонента `i18n` интерполируют сообщения локализации по входному параметру `path`. В примере выше: :::v-pre `{{ $t('tos') }}` ::: интерполируется с сообщением локализации `term`. В примере выше интерполяция компонента использует **формат в виде списка**. Потомки функционального компонента `i18n` интерполируются по порядку их появления. Определить тип корневого элемента можно указать с помощью входного параметра `tag`. Если входной параметр не указан, то по умолчанию будет `'span'`. Также можно указать значение `false`, чтобы вставлять дочерние элементы без создания и оборачивания в корневой. ## Использование синтаксиса слотов :::tip Поддержка с версии :new: 8.14+ ::: Гораздо удобнее использовать синтаксис именованных слотов. Например: ```html
``` ```js const messages = { en: { info: 'You can {action} until {limit} minutes from departure.', change: 'change your flight', refund: 'refund the ticket' } } const i18n = new VueI18n({ locale: 'en', messages }) new Vue({ i18n, data: { changeUrl: '/change', refundUrl: '/refund', changeLimit: 15, refundLimit: 30 } }).$mount('#app') ``` Результат: ```html

You can change your flight until 15 minutes from departure.

``` С версии Vue 2.6 можно использовать сокращённый синтаксис слотов в шаблонах: ```html
``` :::warning Ограничение :warning: В компоненте `i18n` входные параметры слота не поддерживаются. ::: ## Использование синтаксиса places :::danger Внимание! В следующей мажорной версии входные параметры `place` и `places` будут объявлены устаревшими. Рекомендуем использовать синтаксис слотов. ::: :::tip Поддержка с версии :new: 7.2+ ::: :::warning Обратите внимание :warning: В компоненте `i18n` содержимое, состоящее только из пробелов, будет опущено. ::: Именованное форматирование поддерживается с помощью атрибута `place`. Например: ```html
{{ changeLimit }} {{ $t('change') }}
``` ```js const messages = { en: { info: 'You can {action} until {limit} minutes from departure.', change: 'change your flight', refund: 'refund the ticket' } } const i18n = new VueI18n({ locale: 'en', messages }) new Vue({ i18n, data: { changeUrl: '/change', refundUrl: '/refund', changeLimit: 15, refundLimit: 30 } }).$mount('#app') ``` Результат: ```html

You can change your flight until 15 minutes from departure.

``` :::warning Обратите внимание :warning: Для использования именованного форматирования все потомки компонента `i18n` должны иметь установленный атрибут `place`. В противном случае будет использовано форматирование списком. ::: Если всё же необходимо интерполировать текстовое содержимое с помощью именованного форматирования, можно определить свойство `places` на компоненте `i18n`. Например: ```html ``` Результат: ```html

You can refund your ticket until 30 minutes from departure.

``` ================================================ FILE: vuepress/ru/guide/lazy-loading.md ================================================ # Ленивая загрузка переводов Одновременная загрузка всех файлов переводов может быть излишней и ненужной. Ленивая или асинхронная загрузка файлов переводов очень просто реализуется при использовании Webpack. Предположим, что у нас есть каталог проекта следующей структуры: ``` наш-проект-отлично -dist -src --routes --store --setup ---i18n-setup.js --lang ---en.js ---it.js ``` В каталоге `lang` располагаются все файлы переводов. В каталоге `setup` сгруппированы различные файлы настроек, например настройки i18n, регистрация глобальных компонентов, инициализации плагинов и другое. ```js // i18n-setup.js import Vue from 'vue' import VueI18n from 'vue-i18n' import messages from '@/lang/en' import axios from 'axios' Vue.use(VueI18n) export const i18n = new VueI18n({ locale: 'en', // установка локализации fallbackLocale: 'en', messages // установка сообщений локализации }) const loadedLanguages = ['en'] // список локализаций, которые пред-загружены function setI18nLanguage(lang) { i18n.locale = lang axios.defaults.headers.common['Accept-Language'] = lang document.querySelector('html').setAttribute('lang', lang) return lang } export function loadLanguageAsync(lang) { // Если локализация та же if (i18n.locale === lang) { return Promise.resolve(setI18nLanguage(lang)) } // Если локализация уже была загружена if (loadedLanguages.includes(lang)) { return Promise.resolve(setI18nLanguage(lang)) } // Если локализация ещё не была загружена return import( /* webpackChunkName: "lang-[request]" */ `@/i18n/messages/${lang}.js` ).then(messages => { i18n.setLocaleMessage(lang, messages.default) loadedLanguages.push(lang) return setI18nLanguage(lang) }) } ``` Для начала создаём новый экземпляр VueI18n как обычно. Затем определяем массив `loadedLanguages` в котором будем хранить список загруженных языков. Далее создаём функцию `setI18nLanguage`, которая будет переключать локализацию в экземпляре vueI18n, axios и где ещё это необходимо. Функция `loadLanguageAsync` будет использоваться для изменения языка. Загрузка новых файлов осуществляется функцией `import`, которую предоставляет Webpack и позволяет загружать файлы динамически, а поскольку она возвращает Promise, то можем легко дождаться окончания загрузки. Подробнее о динамических импортах можно изучить в [документации Webpack](https://webpack.js.org/guides/code-splitting/#dynamic-imports). Использовать `loadLanguageAsync` очень просто. Например, в хуке beforeEach vue-router. ```js router.beforeEach((to, from, next) => { const lang = to.params.lang loadLanguageAsync(lang).then(() => next()) }) ``` Можно доработать реализацию, например добавив проверку поддерживается ли переданный `lang` или нет и вызывать `reject` чтобы отловить подобные случаи в хуке `beforeEach` и остановить навигацию. ================================================ FILE: vuepress/ru/guide/locale.md ================================================ # Переключение локализации Обычно используют корневой экземпляр Vue в качестве точки истины, а все дочерние компоненты используют свойство `locale` от класса `VueI18n` передаваемого по ссылке. Иногда может потребоваться динамически переключать локализацию. Для этого нужно изменить значение свойства `locale` экземпляра `VueI18n`. ```js const i18n = new VueI18n({ locale: 'ru', // устанавливаем локализацию по умолчанию ... }) // Создаём корневой экземпляр Vue new Vue({ i18n, ... }).$mount('#app') // Переключаем на другую локализацию i18n.locale = 'en' ``` Каждый компонент содержит экземпляр `VueI18n`, ссылающийся на свойство `$i18n`, которое также можно использовать для переключения локализации. Пример: ```vue ``` :::warning Обратите внимание :warning: Изменение локализации игнорируется компонентами с опцией `sync: false`. ::: :::warning Компонент vs. корневая область видимости :warning: Изменение `$i18n.locale` внутри компонента не приводит к изменению корневой локализации. Если вы полагаетесь на корневую локализацию, например, при использовании [корневой запасной локализации](./fallback.html), используйте `$root.$i18n.locale` вместо `$i18n.locale`. ::: ================================================ FILE: vuepress/ru/guide/messages.md ================================================ # Синтаксис сообщений локализации ## Структура Синтаксис сообщений локализации: ```typescript // Как определение Flowtype, синтаксис сообщений перевода аналогичен аннотации BNF type LocaleMessages = { [key: Locale]: LocaleMessageObject } type LocaleMessageObject = { [key: Path]: LocaleMessage } type LocaleMessageArray = LocaleMessage[] type MessageContext = { list: (index: number) => mixed, named: (key: string) => mixed, linked: (key: string) => TranslateResult, values: any, path: string, formatter: Formatter, messages: LocaleMessages, locale: Locale }; type MessageFunction = (ctx: MessageContext) => string; type LocaleMessage = string | MessageFunction | LocaleMessageObject | LocaleMessageArray; type Locale = string type Path = string ``` Используя синтаксис выше, можно создать следующую структуру сообщений локализации: ```json { // локализация 'ru' "ru": { "key1": "это сообщение 1", // обычное использование "nested": { // вложенное "message1": "это вложенное сообщение 1" }, "errors": [ // массив "это сообщение кода ошибки 0", { // объект в массиве "internal1": "это внутреннее сообщение кода ошибки 1" }, [ // массив в массиве "это вложенный массив ошибки 1" ] ] }, // локализация 'en' "en": { // ... } } ``` Для такой структуры сообщений локализации, можно переводить сообщения используя ключи: ```html

{{ $t('key1') }}

{{ $t('nested.message1') }}

{{ $t('errors[0]') }}

{{ $t('errors[1].internal1') }}

{{ $t('errors[2][0]') }}

``` Результат: ```html

это сообщение 1

это вложенное сообщение 1

это сообщение кода ошибки 0

это внутреннее сообщение кода ошибки 1

это вложенный массив ошибки 1

``` ## Связанные сообщения локализации Когда есть ключ с сообщением перевода, которое в точности повторяется в сообщении по другому ключу, то вместо дублирования можно поставить ссылку на него. Для этого к его содержимому нужно добавить префикс `@:` после которого указать полное имя ключа к сообщению перевода, включая пространство имён, к которому делаем ссылку. Сообщения локализации: ```js const messages = { en: { message: { the_world: 'the world', dio: 'DIO:', linked: '@:message.dio @:message.the_world !!!!' } } } ``` Шаблон: ```html

{{ $t('message.linked') }}

``` Результат: ```html

DIO: the world !!!!

``` ### Форматирование связанных сообщений локализации Если важен регистр символов в переводе, то можно управлять регистром связанного сообщения локализации. Связанные сообщения можно отформатировать используя модификатор `@.modifier:key` Доступны следующие модификаторы: * `upper`: Форматирование в верхний регистр всех символов в связанном сообщении. * `lower`: Форматирование в нижний регистр всех символов в связанном сообщении. * `capitalize`: Форматирование заглавной первой буквы в связанном сообщении. Сообщения локализации: ```js const messages = { en: { message: { homeAddress: 'Home address', missingHomeAddress: 'Please provide @.lower:message.homeAddress' } }, ru: { message: { homeAddress: 'Домашний адрес', missingHomeAddress: 'Пожалуйста укажите @.lower:message.homeAddress' } } } ``` ```html

{{ $t('message.missingHomeAddress') }}

``` Результат: ```html

Пожалуйста укажите домашний адрес

``` При необходимости можно добавлять новые модификаторы или перезаписывать существующие через опцию `modifiers` в конструкторе `VueI18n`. ```js const i18n = new VueI18n({ locale: 'ru', modifiers: { // Добавление нового модификатора snakeCase: str => str.split(' ').join('-') }, messages: { // ... }, }) ``` ### Группировка с помощью скобок Ключ связанного сообщения также можно указывать в виде `@:(message.foo.bar.baz)`, где ссылка на другой ключ перевода обрамляется в скобки `()`. Подобное может потребоваться, если за ссылкой на другое сообщение `@:message.something` требуется поставить точку `.`, которая в противном случае считалась бы частью ссылки. Сообщения локализации: ```js const messages = { en: { message: { dio: 'DIO', linked: "There's a reason, you lost, @:(message.dio)." } }, ru: { message: { dio: 'DIO', linked: "Есть причина по которой ты проиграл, @:(message.dio)." } } } ``` Шаблон: ```html

{{ $t('message.linked') }}

``` Результат: ```html

There's a reason, you lost, DIO.

``` ## Функция для сообщения vue-i18n рекомендует использовать строки для формата списком или именованного формата в качестве сообщения локализации при переводе сообщений. Однако бывают ситуации, когда из-за сложного синтаксиса языка, необходима полная мощь возможностей JavaScript. В таком случае, вместо строковых сообщений можно использовать **функцию для сообщения**. Функция ниже просто возвращает приветствие: ```js const messages = { en: { greeting: (ctx) => 'hello!' }, ru: { greeting: (ctx) => 'привет!' } } ``` Использовать функцию для сообщения очень просто! Необходимо просто указать ключ с помощью `$t` или `t`: ```html

{{ $t('greeting') }}

``` Результат будет таким: ```html

привет!

``` Используется возвращаемый результат из функции для сообщения. ### Именованный формат vue-i18n поддерживает [именованный формат](./formatting.md#именованный-формат) для строковых сообщений. vue-i18n интерполирует значения с помощью `$t` или `t` и выводит их. Тоже самое можно сделать и с функцией для сообщения, используя **контекст сообщения**: Пример приветствия: ```js const messages = { en: { greeting: (ctx) => `hello, ${ctx.named('name')}!` }, ru: { greeting: (ctx) => `привет, ${ctx.named('name')}!` } } ``` Шаблон: ```html

{{ $t('greeting', { name: 'DIO' }) }}

``` Результат: ```html

привет, DIO!

``` Контекст сообщения предоставляет доступ к функции `named`. Необходимо указать ключ, указываемым для `$t` или `t`, который разрешится требуемым значением. ### Формат списков Использование формата списков аналогично именованному формату, описанному выше. vue-i18n поддерживает [формат списков](./formatting.md#формат-списков) для строковых сообщений. vue-i18n интерполирует значения с помощью `$t` или `t` и выводит их. Тоже самое можно сделать и с функцией для сообщения, используя **контекст сообщения**: Пример приветствия: ```js const messages = { en: { greeting: (ctx) => `hello, ${ctx.list(0)}!` }, ru: { greeting: (ctx) => `привет, ${ctx.list(0)}!` } } ``` Шаблон: ```html

{{ $t('greeting', ['DIO']) }}

``` Результат: ```html

привет, DIO!

``` Контекст сообщения предоставляет доступ к функции `list`. Необходимо указать ключ, указываемым для `$t` или `t`, который разрешится требуемым значением. ### Ограничение В функции для сообщения следующие возможности, которые доступны в строковом варианте, не будут доступны через контекст сообщения: - Связанные сообщения локализации - Плюрализация ================================================ FILE: vuepress/ru/guide/number.md ================================================ # Локализация чисел :::tip Поддержка с версии :new: 7.0+ ::: Можно выполнять локализацию чисел по соответствующему формату. Пример формата для чисел: ```js const numberFormats = { 'en-US': { currency: { style: 'currency', currency: 'USD' } }, 'ru-RU': { currency: { style: 'currency', currency: 'RUB', currencyDisplay: 'symbol' } } } ``` Как указано выше, можно задать числовые форматы (например, `currency` для валюты) используя [опции ECMA-402 Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat). После этого, для возможности использования данного формата в сообщениях локализации, необходимо задать опцию `numberFormats` конструктора `VueI18n`: ```js const i18n = new VueI18n({ numberFormats }) new Vue({ i18n }).$mount('#app') ``` Шаблон: ```html

{{ $n(100, 'currency') }}

{{ $n(100, 'currency', 'ru-RU') }}

``` Результат: ```html

$100.00

100,00 ₽

``` ## Пользовательское форматирование :::tip Поддержка с версии :new: 8.10+ ::: Метод `$n` возвращает результат в виде строки с полностью отформатированным числом, которую можно использовать лишь целиком. В случаях, когда нужно стилизовать некоторую часть отформатированного числа (например, дробную часть), `$n` будет недостаточно. В таких случаях необходимо использовать функциональный компонент ``. При минимальном наборе свойств `` генерирует тот же результат, что и `$n` обернутый в сконфигурированный DOM-элемент. Шаблон: ```html
``` Результат: ```html
100 $100.00 100,00 ₽
``` Но по-настоящему оценить удобство компонента можно лишь тогда, когда он применяется с использованием [слотов с ограниченной областью видимости](https://ru.vuejs.org/v2/guide/components-slots.html#%D0%A1%D0%BB%D0%BE%D1%82%D1%8B-%D1%81-%D0%BE%D0%B3%D1%80%D0%B0%D0%BD%D0%B8%D1%87%D0%B5%D0%BD%D0%BD%D0%BE%D0%B9-%D0%BE%D0%B1%D0%BB%D0%B0%D1%81%D1%82%D1%8C%D1%8E-%D0%B2%D0%B8%D0%B4%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8). Допустим, есть требование выводить целую часть числа полужирным шрифтом. Реализовать это можно с помощью слота `integer`: ```html {{ slotProps.integer }} ``` Результат: ```html $100.00 ``` Можно использовать несколько слотов одновременно: ```html {{ slotProps.currency }} {{ slotProps.integer }} {{ slotProps.group }} {{ slotProps.fraction }} ``` (Полученный HTML ниже отформатирован для лучшей читаемости) ```html 1 , 234 00 ``` Определить тип корневого элемента можно указать с помощью входного параметра `tag`. Если входной параметр не указан, то по умолчанию будет `'span'`. Также можно указать значение `false`, чтобы вставлять дочерние элементы без создания и оборачивания в корневой. Полный список поддерживаемых слотов, а также другие свойства `` можно найти [на странице справочника API](../api/readme.md#i18n-n-functional-component). ================================================ FILE: vuepress/ru/guide/pluralization.md ================================================ # Плюрализация Для переводимых сообщений есть возможность использовать плюрализацию. Для этого необходимо указывать строки переводов для различных случаев через разделитель `|`. *В шаблоне в таких случаях необходимо использовать метод `$tc()` вместо `$t()`.* Сообщения локализации: ```js const messages = { en: { car: 'car | cars', apple: 'no apples | one apple | {count} apples' }, ru: { car: 'машина | машины', apple: 'нет яблок | одно яблоко | {count} яблок' } } ``` Шаблон: ```html

{{ $tc('car', 1) }}

{{ $tc('car', 2) }}

{{ $tc('apple', 0) }}

{{ $tc('apple', 1) }}

{{ $tc('apple', 10, { count: 10 }) }}

``` Результат: ```html

машина

машины

нет яблок

одно яблоко

10 яблок

``` ## Аргумент для доступа к числу Нет необходимости явно передавать число для плюрализации. В сообщениях локализации число доступно через именованные аргументы `{count}` и/или `{n}`. При желании их можно переопределить. Сообщения локализации: ```js const messages = { en: { apple: 'no apples | one apple | {count} apples', banana: 'no bananas | {n} banana | {n} bananas' }, ru: { apple: 'нет яблок | одно яблоко | {count} яблок', banana: 'нет бананов | {n} банан | {n} бананов' } } ``` Шаблон: ```html

{{ $tc('apple', 10, { count: 10 }) }}

{{ $tc('apple', 10) }}

{{ $tc('banana', 1, { n: 1 }) }}

{{ $tc('banana', 1) }}

{{ $tc('banana', 100, { n: 'слишком много' }) }}

``` Результат: ```html

10 яблок

10 яблок

1 банан

1 банан

слишком много бананов

``` ## Пользовательская плюрализация Стандартная реализация плюрализации не подходит для некоторых языков (к примеру, в славянских языках другие правила множественности). Можно предоставить собственную реализацию, передав `pluralizationRules` в конструктор `VueI18n`. Упрощенный пример для славянских языков (Русский, Украинский и другие): ```js new VueI18n({ // Ключ - язык, для которого будет применяться правило, в этом примере - `'ru'` // Value - функция плюрализации pluralizationRules: { /** * @param choice {number} индекс выбора, переданный в $tc: `$tc('path.to.rule', choiceIndex)` * @param choicesLength {number} общее количество доступных вариантов * @returns финальный индекс для выбора соответственного варианта слова */ 'ru': function(choice, choicesLength) { // this === VueI18n экземпляра, так что свойство locale также существует здесь if (choice === 0) { return 0; } const teen = choice > 10 && choice < 20; const endsWithOne = choice % 10 === 1; if (choicesLength < 4) { return (!teen && endsWithOne) ? 1 : 2; } if (!teen && endsWithOne) { return 1; } if (!teen && choice % 10 >= 2 && choice % 10 <= 4) { return 2; } return (choicesLength < 4) ? 2 : 3; } } }) ``` Такая реализация позволит использовать: ```js const messages = { ru: { car: '0 машин | {n} машина | {n} машины | {n} машин', banana: 'нет бананов | {n} банан | {n} банана | {n} бананов' } } ``` Для такого формата вариантов `0 вещей | количество вещей заканчивается на 1 | количество вещей заканчивается на 2-4 | количество вещей заканчивается на 5-9, 0 и числа от 11 до 19`. P.S. Славянское множественное число - сложное явление, подробнее о нем можно прочитать [здесь](http://www.russianlessons.net/lessons/lesson11_main.php). В шаблоне, по-прежнему, необходимо использовать `$tc()` вместо `$t()`: ```html

{{ $tc('car', 1) }}

{{ $tc('car', 2) }}

{{ $tc('car', 4) }}

{{ $tc('car', 12) }}

{{ $tc('car', 21) }}

{{ $tc('banana', 0) }}

{{ $tc('banana', 4) }}

{{ $tc('banana', 11) }}

{{ $tc('banana', 31) }}

``` Результат: ```html

1 машина

2 машины

4 машины

12 машин

21 машина

нет бананов

4 банана

11 бананов

31 банан

``` ### Плюрализация по умолчанию Если для используемой локали не предоставить правило плюрализации, [по умолчанию](#плюрализация) будет использовано правило для английского языка ================================================ FILE: vuepress/ru/guide/sfc.md ================================================ # Однофайловые компоненты ## Базовое использование В компоненте Vue или приложении Vue с использованием однофайловых компонентов, можно управлять сообщениями локализации с помощью пользовательского блока `i18n`. Код компонента из [примера использования с однофайловыми компонентами](https://github.com/kazupon/vue-i18n/tree/dev/examples/sfc): ```vue { "en": { "hello": "hello world!" }, "ru": { "hello": "Привет мир!" } } ``` ## Установка vue-i18n-loader Требуется установить `vue-loader` и `vue-i18n-loader` чтобы использовать пользовательские блоки ``. Скорее всего [vue-loader](https://github.com/vuejs/vue-loader) уже используется в проекте, если уже работаете с однофайловыми компонентами, но необходимо дополнительно установить [vue-i18n-loader](https://github.com/kazupon/vue-i18n-loader): ```bash npm i --save-dev @kazupon/vue-i18n-loader ``` ## Webpack Для Webpack требуется следующая конфигурация: Для vue-loader v15 или более поздних версий: ```js module.exports = { // ... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader' }, { resourceQuery: /blockType=i18n/, type: 'javascript/auto', loader: '@kazupon/vue-i18n-loader' } // ... ] } // ... } ``` Для vue-loader v14: ```js module.exports = { // ... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { // необходимо указать ключ `i18n` для загрузчика `vue-i18n-loader` // (https://github.com/kazupon/vue-i18n-loader) i18n: '@kazupon/vue-i18n-loader' } } } // ... ] } // ... } ``` ## Vue CLI 3.0 [Vue CLI 3.0](https://github.com/vuejs/vue-cli) скрывает конфигурацию Webpack, поэтому для добавления поддержки тегов `` в однофайловых компонентах необходимо изменить существующую конфигурацию. Для этого нужно создать файл `vue.config.js` в корне проекта и добавить в него следующее: Для vue-loader v15 или более поздних версий: ```js module.exports = { chainWebpack: config => { config.module .rule('i18n') .resourceQuery(/blockType=i18n/) .type('javascript/auto') .use('i18n') .loader('@kazupon/vue-i18n-loader') .end() } } ``` Для vue-loader v14: ```js const merge = require('deepmerge') module.exports = { chainWebpack: config => { config.module .rule('vue') .use('vue-loader') .tap(options => merge(options, { loaders: { i18n: '@kazupon/vue-i18n-loader' } }) ) } } ``` _Не забудьте установить [deepmerge](https://github.com/KyleAMathews/deepmerge)! (`npm i deepmerge -D` или `yarn add deepmerge -D`)_ Подробнее о возможностях изменения существующей конфигурации Webpack можно изучить [здесь](https://cli.vuejs.org/ru/guide/webpack.html). ## Laravel-Mix Для Laravel-mix 4 с vue-loader v15 или более поздней версии: ```js // Расширяем Mix с помощью метода "i18n", который загрузит vue-i18n-loader mix.extend( 'i18n', new class { webpackRules() { return [ { resourceQuery: /blockType=i18n/, type: 'javascript/auto', loader: '@kazupon/vue-i18n-loader', }, ]; } }(), ); // Убедитесь что вызвали .i18n() (для загрузки загрузчика) перед .js(..., ...) mix.i18n() .js( 'resources/js/App.js', 'public/js/app.js' ) ... ``` Для Laravel-mix 2 с vue-loader v14: В Laravel-mix, начиная с версии [V2.1](https://github.com/JeffreyWay/laravel-mix/releases/tag/v2.1), можно добавлять пользовательские правила с помощью `mix.extend()`. Laravel-mix уже имеет собственные правила для обработки `.vue` файлов. Чтобы добавить `vue-i18n-loader`, нужно добавить в `webpack.mix.js` следующее: ```js // Код ниже внедрит загрузчик i18n (@kazupon/vue-i18n-loader) в качестве загрузчика .vue файлов. mix.extend( 'i18n', function( webpackConfig, ...args ) { webpackConfig.module.rules.forEach( ( module ) => { // Поиск компонента "vue-loader", который обрабатывает .vue файлы. if( module.loader !== 'vue-loader' ) { return; } // В этом модуле добавляем vue-i18n-loader для тега i18n. module.options.loaders.i18n = '@kazupon/vue-i18n-loader'; } ); } ); // Убедитесь что вызвали .i18n() (для загрузки загрузчика) перед .js(..., ...) mix.i18n() .js( 'resources/assets/js/App.js', 'public/js/app.js' ) ... ``` ## Загрузка YAML Пользовательские блоки `i18n` можно указывать в формате `JSON` или `YAML` (используя функцию предварительного загрузчика `vue-loader`). Пользовательский блок `i18n` в формате `YAML`: ```vue en: hello: "hello world!" ru: hello: "привет мир!" ``` Конфигурация Webpack: Для vue-loader v15 или более поздних версий: ```js // Vue CLI 3.0 module.exports = { chainWebpack: config => { config.module .rule('i18n') .resourceQuery(/blockType=i18n/) .type('javascript/auto') .use('i18n') .loader('@kazupon/vue-i18n-loader') .end() .use('yaml') .loader('yaml-loader') .end() } } ``` Для vue-loader v14: ```js module.exports = { // ... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { preLoaders: { i18n: 'yaml-loader' }, loaders: { i18n: '@kazupon/vue-i18n-loader' } } } // ... ] } // ... } ``` ## Несколько пользовательских блоков Можно использовать сообщения локализации из нескольких пользовательских блоков `i18n`. ```vue { "en": { "hello": "hello world!" }, "ru": { "hello": "Привет мир!" } } ``` В примере выше, первый пользовательский блок загружает общие сообщения локализации с помощью атрибута `src`, второй пользовательский блок загружает сообщения локализации, которые определены только в этом однофайловом компоненте. Все они будут объединены в качестве сообщений локализации компонента. Несколько пользовательских блоков полезны, когда использовать их в качестве модулей. ## Локальные стили При использовании `vue-i18n` с локальными стилями (`style scoped`) необходимо помнить и использовать [глубокий селектор](https://vue-loader.vuejs.org/ru/guide/scoped-css.html#%D0%BA%D0%BE%D1%80%D0%BD%D0%B5%D0%B2%D0%BE%D0%B9-%D1%8Dn%D0%B5%D0%BC%D0%B5%D0%BD%D1%82-%D0%B4%D0%BE%D1%87%D0%B5%D1%80%D0%BD%D0%B5%D0%B3%D0%BE-%D0%BA%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%B5%D0%BD%D1%82%D0%B0) для стилизации элемента __*внутри*__ строки перевода. Например: __Когда перевод содержит только текст__ (работает без глубокого селектора) ```vue { "en": { "hello": "hello world!" }, "ru": { "hello": "Привет мир!" } } ``` __Когда перевод содержит HTML-элемент__ (необходимо использовать глубокий селектор) ```vue { "en": { "hello": "helloworld!" }, "ru": { "hello": "привет мир!" } } ``` ## Пользовательские блоки в функциональном компоненте Если в шаблоне однофайловых компонентов используется функциональный компонент и определены пользовательские блоки `i18n`, то обратите внимание что невозможно локализовать с помощью сообщений локализации. Например, следующий код не может использовать сообщения локализации из блока `i18n`. ```vue { "en": { "hello": "hello world" }, "ru": { "hello": "привет мир" } } ``` ================================================ FILE: vuepress/ru/guide/tooling.md ================================================ # Инструментарий Для поддержки i18n приложений Vue некоторые инструменты предоставляются официально. Также есть инструменты от сторонних разработчиков, которые интегрируются в Vue I18n. ## Официальный инструментарий ### Плагин для Vue CLI [vue-cli-plugin-i18n](https://github.com/kazupon/vue-cli-plugin-i18n) — официальный плагин для Vue CLI. С помощью этого плагина можно настроить среду i18n для приложения Vue и поддерживать среду разработки i18n. ### Модуль для Nuxt [nuxt-i18n](https://github.com/nuxt-community/nuxt-i18n/) — соответствующий модуль для Nuxt.js. ### Загрузчик для Webpack [vue-i18n-loader](https://github.com/kazupon/vue-i18n-loader) — официальный загрузчик для webpack. С помощью этого загрузчика можно использовать пользовательские блоки `i18n` в однофайловых компонентах. Подробнее о пользовательских блоках `i18n` можно изучить в разделе [Однофайловых компонентов](./sfc.md) ### Плагин для ESLint [eslint-plugin-vue-i18n](https://intlify.github.io/eslint-plugin-vue-i18n/) — ESLint-плагин для Vue I18n. Позволяет легко интегрировать функции проверки локализацией в ваше приложение Vue.js. ### Расширения [vue-i18n-extensions](https://github.com/kazupon/vue-i18n-extensions) — предоставляет некоторые расширения дляVue I18n. Эти расширения позволяет использовать в рендеринге на стороне сервера (SSR) и улучшить производительность i18n. ## Сторонние разработки ### BabelEdit [BabelEdit](https://www.codeandweb.com/babeledit) — редактор переводов для веб-приложений. BabelEdit может переводить файлы `json`, а также умеет работать с пользовательскими блоками `i18n` однофайловых компонентов. Подробнее про BabelEdit можно узнать [на странице введения](https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-vue-app-with-vue-i18n). ### i18n Ally [i18n Ally](https://marketplace.visualstudio.com/items?itemName=antfu.i18n-ally) — расширение i18n для VSCode. i18n Ally предоставляет потрясающий DX для разработки с использованием i18n. Подробнее о расширении i18n Ally можно изучить в [README](https://github.com/antfu/i18n-ally/blob/master/README.md). ### i18nPlugin (платформа intellij) [i18nPlugin](https://github.com/nyavro/i18nPlugin) — плагин Intellij idea для поддержки i18next ([Jetbrains plugin page](https://plugins.jetbrains.com/plugin/12981-i18n-support)). Плагин для i18n typescript/javascript/PHP. Поддерживает vue-i18n. Для включения поддержки vue-i18n в настройках -> Tools -> i18n Plugin configuration выберите "Vue-i18n". Необходимо установить каталоги с файлами локализаций (по умолчанию locales). ### vue-i18n-extract [vue-i18n-extract](https://github.com/pixari/vue-i18n-extract) выполняет статический анализ проекта Vue.js на основе vue-i18n и сообщает следующую информацию: - список всех **неиспользуемых ключей vue-i18n** (записи, найденные в файлах перевода, но не использованные в проекте) - список всех **пропущенных ключей** (записи, найденные в проекте, но отсутствующие в файлах перевода) Имеется возможность отобразить результат в консоли или записать его в файл json. Пропущенные ключи также могут быть автоматически добавлены в заданные файлы переводов. ================================================ FILE: vuepress/ru/installation.md ================================================ # Установка ## Примечание совместимости - Vue.js версии `2.0.0`+ ## Загрузка файла / CDN Сервис [unpkg.com](https://unpkg.com) предоставляет CDN-ссылки на основе NPM-пакетов. Ссылка выше будет всегда указывать на последнюю версию на NPM. Можно использовать конкретную версию или тег с помощью URL следующего вида При подключении vue-i18n после Vue плагин установит себя автоматически: ```html ``` ## NPM ```bash npm install vue-i18n ``` ## Yarn ```bash yarn add vue-i18n ``` При использовании системы модулей нужно явно устанавливать `vue-i18n` через `Vue.use()`: ```js import Vue from 'vue' import VueI18n from 'vue-i18n' Vue.use(VueI18n) ``` Подобного не требуется делать при подключении через глобальный тег `

{{ $t("message.hello") }}

``` ## JavaScript ```js // При использовании модульной системы (например, через vue-cli) // нужно импортировать Vue и VueI18n и вызвать Vue.use(VueI18n). // // import Vue from 'vue' // import VueI18n from 'vue-i18n' // // Vue.use(VueI18n) // Готовые переводы сообщений локализаций const messages = { en: { message: { hello: 'hello world' } }, ru: { message: { hello: 'Привет мир' } } } // Создание экземпляра VueI18n с настройками const i18n = new VueI18n({ locale: 'ru', // установка локализации по умолчанию messages // установка сообщений локализаций }) // Создание экземпляра Vue с опцией `i18n` new Vue({ i18n }).$mount('#app') // Теперь можно запускать приложение! ``` Результат будет таким: ```html

Привет мир

``` ================================================ FILE: vuepress/started.md ================================================ # Getting started :::tip NOTE We will be using [ES2015](https://github.com/lukehoban/es6features) in the code samples in the guide. ::: ## HTML ```html

{{ $t("message.hello") }}

``` ## JavaScript ```js // If using a module system (e.g. via vue-cli), import Vue and VueI18n and then call Vue.use(VueI18n). // import Vue from 'vue' // import VueI18n from 'vue-i18n' // // Vue.use(VueI18n) // Ready translated locale messages const messages = { en: { message: { hello: 'hello world' } }, ja: { message: { hello: 'こんにちは、世界' } } } // Create VueI18n instance with options const i18n = new VueI18n({ locale: 'ja', // set locale messages, // set locale messages }) // Create a Vue instance with `i18n` option new Vue({ i18n }).$mount('#app') // Now the app has started! ``` Output the following: ```html

こんにちは、世界

``` ================================================ FILE: vuepress/zh/README.md ================================================ --- home: true heroImage: ./../vue-i18n-logo.png actionText: 快速上手 → actionLink: introduction.md footer: MIT Licensed | Copyright © 2020 kazuya kawaguchi ---

🥇 金牌赞助商

Nuxt.js

🥈 白银赞助商

适用于应用程序(Web应用程序)的BabelEdit翻译编辑器

🥉 青铜赞助商

zenarchitects sendcloud

简单

通过简单的 API 将你的应用国际化

强大

除了简单的翻译外,还支持复数,数字,日期时间等本地化处理

面向组件

你可以在单文件组件上管理语言环境信息

================================================ FILE: vuepress/zh/api/README.md ================================================ --- sidebar: auto --- # API参考 ## 扩展 Vue ### Vue 构造函数选项 #### i18n * **类型:** `I18nOptions` 基于组件的本地化选项 * **请参阅:** `VueI18n` 类构造函数选项 ### Vue 注入方法 #### $t * **参数** * `{Path} key`:必填 * `{Locale} locale`:可选 * `{Array | Object} values`:可选 * **返回值:**`TranslateResult` 本地化语言环境信息 `key`,在本地化时组件的语言环境信息优先于全局语言环境信息。如果未指定组件的语言环境信息,就使用全局语言环境信息进行本地化。如果你指定了 `locale` 参数,则使用 `locale` 提供的语言环境进行本地化。如果你为列表/格式化的语言环境信息指定了 `key`,就必须同时指定 `values`。有关 `values` 的详细信息,请参阅[格式化](../guide/formatting.md)。 :::danger 提示 注意,你需要在生命周期方法中保证上下文是组件实例 (例如在 `data` 选项中,`const $t = this.$t.bind(this)`)。 ::: #### $tc * **参数:** * `{Path} key`:必填 * `{number} choice`:可选,默认为 1 * `{Locale} locale`:可选 * `{string | Array | Object} values`:可选 * **返回值:**`TranslateResult` 以复数形式将语言环境信息 `key` 本地化。在本地化时组件的语言环境信息优先于全局语言环境信息。如果未指定组件的语言环境信息,就使用全局语言环境信息进行本地化。如果你指定了 `locale` 参数,则使用 `locale` 提供的语言环境进行本地化。如果将 `values` 指定为字符串,则该字符串会作为语言环境信息进行本地化。如果将 `values` 指定为 Array 或 Object,则格式必须为 `$t` 的 `values`。 :::danger 提示 注意,你需要在生命周期方法中保证上下文是组件实例 (例如在 `data` 选项中,`const $tc = this.$tc.bind(this)`) ::: #### $te * **参数:** * `{Path} key`:必填 * `{Locale} locale`:可选 * **返回值:**`boolean` 检查 key 是否存在。在 Vue 实例中,如果未指定组件语言环境信息,则使用全局语言环境信息。如果指定了 `locale`,则使用 `locale` 的语言环境。 :::danger 提示 注意,你需要在生命周期方法中保证上下文是组件实例 (例如在 `data` 选项中,`const $te = this.$te.bind(this)`)。 ::: #### $d > :new: 7.0+ 新增 * **参数:** * `{number | Date} value`:必填 * `{Path | Object} key`:可选 * `{Locale | Object} locale`:可选 * **返回值:**`DateTimeFormatResult` 将日期时间 `value` 以 `key` 的格式本地化。日期时间格式 `key` 需要注册到 `VueI18n` 类的 `dateTimeFormats` 选项,并依赖于 `VueI18n` 构造函数的 `locale` 选项。如果要指定 `locale` 参数,它将优先于 `VueI18n` 构造函数的 `locale` 选项。 如果 `dateTimeFormats` 选项中不存在日期时间格式的 `key`,则根据 `VueI18n` 构造函数的 `fallbackLocale` 选项回退。 :::danger 提示 注意,你需要在生命周期方法中保证上下文是组件实例 (例如在 `data` 选项中,`const $d = this.$d.bind(this)`)。 ::: #### $n > :new: 7.0+ 新增 * **参数:** * `{number} value`:必填 * `{Path | Object} key`:可选 * `{Locale} locale`:可选 * **返回值:**`NumberFormatResult` 将数字 `value` 以 `key` 的格式本地化。数字格式 `key` 需要注册到 `VueI18n` 类的 `numberFormats` 选项,并依赖于 `VueI18n` 构造函数的 `locale` 选项。如果要指定 `locale` 参数,它将优先于 `VueI18n` 构造函数的 `locale` 选项。 如果 `numberFormats` 选项中不存在用数字格式 `key`,则根据 `VueI18n` 构造函数的 `fallbackLocale` 选项回退。 如果第二个 `key` 参数指定为对象,则它应具有以下属性: * `key {Path}`:可选,数字格式 * `locale {Locale}`:可选,语言环境 * `style {string}`:可选,数字格式选项 * `currency {string}`:可选,数字格式选项 * `currencyDisplay {string}`:可选,数字格式选项 * `useGrouping {boolean}`:可选,数字格式选项 * `minimumIntegerDigits {string}`:可选,数字格式选项 * `minimumFractionDigits {string}`:可选,数字格式选项 * `maximumFractionDigits {string}`:可选,数字格式选项 * `minimumSignificantDigits {string}`:可选,数字格式选项 * `maximumSignificantDigits {string}`:可选,数字格式选项 * `localeMatcher {string}`:可选,数字格式选项 * `formatMatcher {string}`:可选,数字格式选项 任何指定数字的格式选项将优先于 `VueI18n` 构造函数的 `numberFormats`。 :::danger 提示 注意,你需要在生命周期方法中保证上下文是组件实例 (例如在 `data` 选项中,`const $n = this.$n.bind(this)`)。 ::: ### 注入属性 #### $i18n * **类型:**`I18n` * **只读** 若已经指定了 `VueI18n` 实例,则将其返回。 如果在组件选项中指定了 `i18n` 选项,则可以在组件上获得 `VueI18n` 实例,否则,你将获得 `VueI18n` 的根实例。 ## `VueI18n` 类 `Vuei18n` 类实现了 `I18n` [flowtype 接口](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js) ### 静态属性 #### version * **类型:**`string` vue-i18n 版本 #### availabilities > :new: 7.0+ 新增 * **类型:**`IntlAvailability` 是否提供以下国际化功能: * `{boolean} dateTimeFormat`:环境敏感的时间格式 * `{boolean} numberFormat`:环境敏感的数字格式 由于使用 ECMAScript Internationalization API (ECMA-402) 实现,上述国际化功能取决于[浏览器环境](http://kangax.github.io/compat-table/esintl/)。 ### 构造函数选项 你可以基于[flowtype 定义](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js) 中的 `I18nOptions` 指定下列构造函数选项 #### locale * **类型:**`Locale` * **默认值:**`'en-US'` 语言环境。 #### fallbackLocale * **类型:**`Locale` * **默认值:**`'en-US'` 预设的语言环境。 #### messages * **类型:**`LocaleMessages` * **默认值:**`{}` 本地化的语言环境信息。 #### dateTimeFormats > :new: 7.0+ 新增 * **类型:**`DateTimeFormats` * **默认值:**`{}` 本地化的日期时间格式。 * **请参阅:**`DateTimeFormats` 类型的[flowtype 接口](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js) #### numberFormats > :new: 7.0+ 新增 * **类型:**`NumberFormats` * **默认值:**`{}` 本地化的数字格式。 * **请参阅:**`NumberFormats` 类型的[flowtype 接口](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js) #### availableLocales > :new: 8.9.0+ 新增 * **类型:**`Locale[]` * **默认值:**`[]` * **示例:**`["en", "ja"]` 以词法顺序排列的 `messages` 中的可用语言环境列表。 #### formatter * **类型:**`Formatter` * **默认值:** Built in formatter 使用 `Formatter` 接口实现的格式化。 #### missing * **类型:**`MissingHandler` * **默认值:**`null` 缺少本地化时的处理函数。该处理函数在被调用时会使用本地化目标语言环境,本地化路径关键字和 Vue 实例。 如果设置了该函数,则本地化信息未定义时不会产生警告。 #### fallbackRoot * **类型:**`Boolean` * **默认值:**`true` 在组件本地化中,当本地化失败时是否回退到根级别 (全局) 本地化。 如果为 `false`,则会发出警告,并返回 key。 #### fallbackRootWithEmptyString > :new: 8.26+ 新增 * **Type:** `Boolean` * **Default:** `true` 在组件本地化中,当本地化文本为空字符串时,是否回退到根级别 (全局) 本地化。 请注意,vue-i18n 9.x版本的默认行为是对空字符串本地化文本进行回退到根级别本地化。 如果为`false`,则空的本地化文本将不会回退到根目录,并将保留为空字符串。 #### sync * **类型:**`Boolean` * **默认值:**`true` 是否将根级别语言环境与组件本地化语言环境同步。 如果为 `false`,则无论根级别语言环境如何,都要为每个组件语言环境进行本地化。 #### silentTranslationWarn > 6.1+ 新增 * **类型:**`Boolean` * **默认值:**`false` 是否取消本地化失败时输出的警告。 如果为 `true`,则禁止本地化失败警告。 #### silentFallbackWarn > :new: 8.8 新增 * **类型:**`Boolean` * **默认值:**`false` 是否在回退到 `fallbackLocale` 或 `root` 时取消警告。 如果为 `true`,则仅在根本没有可用的转换时生成警告,而不是在回退时。 #### pluralizationRules > 8.5+ * **Type:** `PluralizationRules` * **Default:** `{}` A set of rules for word pluralization in a following format: ```js { // Key - locale for the rule to be applied to. // Value - mapping function that maps a choice index from `$tc` to the actual choice of the plural word. (See getChoiceIndex for details) 'pt': function(choice, choiceIndex) => Number/* index of the plural word */; 'ru': function(choice, choiceIndex) => Number/* index of the plural word */; 'en': function(choice, choiceIndex) => Number/* index of the plural word */; 'jp': function(choice, choiceIndex) => Number/* index of the plural word */; } ``` #### preserveDirectiveContent > 8.7+ 新增 * **类型:**`Boolean` * **默认值:**`false` 在指令解除绑定后,`v-t` 指令的元素是否应该保留 `textContent`。 ### Properties #### locale * **类型:**`Locale` * **可读/可写** 语言环境。 #### fallbackLocale * **类型:**`Locale` * **可读/可写** 预设的语言环境。 #### messages * **类型:**`LocaleMessages` * **只读** 本地化的语言环境信息。 #### dateTimeFormats > :new: 7.0+ 新增 * **类型:**`DateTimeFormats` * **只读** 本地化的日期时间格式。 #### numberFormats > :new: 7.0+ 新增 * **类型:**`NumberFormats` * **只读** 本地化的数字格式。 #### missing * **类型:**`MissingHandler` * **可读/可写** 缺少本地化时的处理函数。 #### formatter * **类型:**`Formatter` * **可读/可写** 使用 `Formatter` 接口实现的格式化。 #### silentTranslationWarn > 6.1 新增 * **类型:**`boolean` * **可读/可写** 是否取消本地化失败时输出的警告。 #### pluralizationRules > 8.5+ * **Type:** `PluralizationRules` * **Read/Write** A set of locale-dependent rules for word pluralization. #### preserveDirectiveContent > 8.7+ 新增 * **类型:**`boolean` * **可读/可写** 在指令解除绑定后,`v-t` 指令的元素是否应该保留 `textContent`。 ### 方法 #### getChoiceIndex * **参数:** * `{number} choice` * `{number} choicesLength` * **返回值:**`finalChoice {number}` 根据当前的数字和一组给定的选项,获取其复数索引,可以通过原型变更覆盖: ```js VueI18n.prototype.getChoiceIndex = /* 自定义实现 */ ``` However, for most usages [pluralizationRules constructor option](#pluralizationrules) should be enough. #### getLocaleMessage( locale ) * **参数:** * `{Locale} locale` * **返回值:**`LocaleMessageObject` 获取语言环境的 `locale` 信息。 #### setLocaleMessage( locale, message ) * **参数:** * `{Locale} locale` * `{LocaleMessageObject} message` 设置语言环境的 `locale` 信息。 #### mergeLocaleMessage( locale, message ) > 6.1+ 新增 * **参数:** * `{Locale} locale` * `{LocaleMessageObject} message` 将语言环境信息 `locale` 合并到已注册的语言环境信息中。 #### t( key, [locale], [values] ) * **参数:** * `{Path} key`:必填 * `{Locale} locale`:可选 * `{Array | Object} values`:可选 * **返回值:**:`TranslateResult` 这与 `$t` 方法返回的 `Function` 相同。更多细节见[$t](#t)。 #### tc( key, [choice], [values] ) * **参数:** * `{Path} key`:必填 * `{number} choice`:可选,默认为 1 * `{string | Array | Object} values`:可选 * **返回值:**`TranslateResult` 这与 `$tc` 方法返回的 `Function` 相同。更多细节见[$tc](#tc)。 #### te( key, [locale] ) * **参数:** * `{string} key`:必填 * `{Locale} locale`:可选 * **返回值:**`boolean` 检查全局语言环境信息中是否存在键名路径。如果指定了 `locale`,请检查语言环境信息 `locale`。 #### getDateTimeFormat ( locale ) > :new: 7.0+ 新增 * **参数:** * `{Locale} locale` * **返回值:**`DateTimeFormat` 获取语言环境的日期时间格式。 #### setDateTimeFormat ( locale, format ) > :new: 7.0+ 新增 * **参数:** * `{Locale} locale` * `{DateTimeFormat} format` 设置语言环境的日期时间格式。 #### mergeDateTimeFormat ( locale, format ) > :new: 7.0+ 新增 * **参数:** * `{Locale} locale` * `{DateTimeFormat} format` 将已注册的日期时间格式与语言环境的日期时间格式合并。 #### d( value, [key], [locale] ) > :new: 7.0+ 新增 * **参数:** * `{number | Date} value`:必填 * `{Path | Object} key`:可选 * `{Locale | Object} locale`:可选 * **返回值:**`DateTimeFormatResult` 这与 Vue 实例方法的 `$d` 方法相同。更多细节见[$d](#d)。 #### getNumberFormat ( locale ) > :new: 7.0+ 新增 * **参数:** * `{Locale} locale` * **返回值:**`NumberFormat` 获取语言环境的数字格式。 #### setNumberFormat ( locale, format ) > :new: 7.0+ 新增 * **参数:** * `{Locale} locale` * `{NumberFormat} format` 设置语言环境的数字格式。 #### mergeNumberFormat ( locale, format ) > :new: 7.0+ 新增 * **参数:** * `{Locale} locale` * `{NumberFormat} format` 将已注册的数字格式与语言环境的数字格式合并。 #### n( value, [key], [locale] ) > :new: 7.0+ 新增 * **参数:** * `{number} value`:必填 * `{Path | Object} key`:可选 * `{Locale} locale`:可选 * **返回值:**`NumberFormatResult` 这与 Vue 实例方法的 `$n` 方法相同。更多细节见[$n](#n)。 ## 指令 > :new: 7.3+ 新增 ### v-t * **预期:**`string | Object` * **修饰符:** * `.preserve`:(8.7.0 新增) 当指令解除绑定时,保留元素 `textContent`。 * **详细:** 更新使用语言环境信息进行本地化的元素 `textContent`。你可以使用字符串语法或对象语法。字符串语法可以指定为语言环境信息的关键字路径。如果可以使用对象语法,则需要将以下参数指定为对象键: * `path`:必填,语言环境信息的关键字 * `locale`:可选,语言环境 * `args`:可选,用于列表或命名格式 ::::tip 注意 当 `v-t` 指令解除绑定时,默认情况下将清除元素 `textContent`。在[过渡动画](https://cn.vuejs.org/v2/guide/transitions.html)内部使用的时候,可能出现不合预期的情况。为了在指令解除绑定之后保留 `textContent` 数据,可使用 `.preserve` 修饰符或全局的 [`preserveDirectiveContent` 选项](#preservedirectivecontent)。 :::: * **示例:** ```html

``` * **请参阅:**[自定义指令本地化](../guide/directive.md) ## 组件 ### i18n 函数式组件 > :new: 7.0+ 新增 #### 参数: * `path {Path}`:必填,关于语言环境信息的键名路径 * `locale {Locale}`:可选,语言环境 * `tag {string}`:可选,默认值 `span` * `places {Array | Object}`:可选 (7.2 新增) #### 用法: ```html ``` ```js const messages = { en: { tos: 'Term of Service', term: 'I accept xxx {0}.' }, ja: { tos: '利用規約', term: '私は xxx の{0}に同意します。' } } const i18n = new VueI18n({ locale: 'en', messages }) new Vue({ i18n, data: { url: '/term' } }).$mount('#app') ``` #### 请参阅: [组件插值](../guide/interpolation.md) ## 特殊属性 ### place > :new: 7.2+ 新增 #### 预期:`{number | string}` 用于组件插槽,指示格式列表的索引值或具名格式的关键字。 有关详细用法,请参阅下面链接的指南部分。 #### 请参阅: [组件插值](../guide/interpolation.md) ================================================ FILE: vuepress/zh/guide/component.md ================================================ # 基于组件的本地化 通常语言环境信息 (例如:`locale`、`messages` 等) 会被设置为 `VueI18n` 实例的构造函数选项,并且该实例会被作为 `i18n` 选项设置在 Vue 的根实例上。 因此你可以全局地在 Vue 的根实例以及任何被组合的组件中使用 `$t` 或者 `$tc` 进行翻译。当然面向 Vue 组件的设计,你也可以更方便的分别控制每个组件的语言环境信息。 基于组件的本地化示例: ```js // 为 Vue 的根实例设置语言环境信息 const i18n = new VueI18n({ locale: 'ja', messages: { en: { message: { hello: 'hello world', greeting: 'good morning' } }, ja: { message: { hello: 'こんにちは、世界', greeting: 'おはようございます' } } } }) // 定义组件 const Component1 = { template: `

Component1 locale messages: {{ $t("message.hello") }}

Fallback global locale messages: {{ $t("message.greeting") }}

`, i18n: { // `i18n` 选项,为组件设置语言环境信息 messages: { en: { message: { hello: 'hello component1' } }, ja: { message: { hello: 'こんにちは、component1' } } } } } new Vue({ i18n, components: { Component1 } }).$mount('#app') ``` 模板: ```html

{{ $t("message.hello") }}

``` 输出如下: ```html

こんにちは、世界

Component1 locale messages: こんにちは、component1

Fallback global locale messages: おはようございます

``` 在上面的例子中,如果组件没有语言环境信息,它将回退到全局定义的本地化信息。组件使用根实例中设置的语言 (在上面的例子中:`locale: 'ja'`)。 注意,在默认情况下,回退到根语言环境会在控制台中生成两个警告: ``` [vue-i18n] Value of key 'message.greeting' is not a string! [vue-i18n] Fall back to translate the keypath 'message.greeting' with root locale. ``` 为避免以上警告 (同时保留那些完全没有翻译给定关键字的警告) 需初始化 `VueI18n` 实例时设置 `silentFallbackWarn:true`。 如果你希望在组件语言环境中进行本地化,可以在 `i18n` 选项中用 `sync: false` 和 `locale`。 ## 组件的共享语言环境消息 有时您可能想为某些组件导入共享的语言环境消息,而不是从全局语言环境消息(例如,组件某些功能的常用消息)回退。 您可以使用 `i18n` 的 `sharedMessages` 选项。 通用语言环境消息示例: ```js export default { en: { buttons: { save: "Save", // ... } }, ja: { buttons: { save: "保存", // ... } } } ``` Components: ```js import commonMessage from './locales/common' // 导入通用语言环境消息 export default { name: 'ServiceModal', template: ` `, i18n: { messages: { ... }, sharedMessages: commonMessages } } ``` 如果将 `sharedMessages` 选项与 `messages` 选项一起指定,则这些消息将被合并为语言环境消息,并进入目标组件的VueI18n实例。 ## 函数式组件的翻译 使用函数式组件时,所有数据 (包括 prop、子内容、插槽、父级内容等) 都通过包含属性的 `context` 传递,并且它无法识别 `this` 的范围,因此在函数式组件上使用 vue-i18n 时,你必须将 `$t` 称为 `parent.$t`,请查看以下示例: ```html ... ... ``` ================================================ FILE: vuepress/zh/guide/datetime.md ================================================ # 日期时间本地化 :::tip 支持版本 :new: 7.0+ 新增 ::: 你可以使用你定义的格式来本地化日期时间。 日期时间格式如下: ```js const dateTimeFormats = { 'en-US': { short: { year: 'numeric', month: 'short', day: 'numeric' }, long: { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long', hour: 'numeric', minute: 'numeric' } }, 'ja-JP': { short: { year: 'numeric', month: 'short', day: 'numeric' }, long: { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long', hour: 'numeric', minute: 'numeric', hour12: true } } } ``` 如上,你可以定义具名的 (例如:`short`、`long` 等) 日期时间格式,并需要使用 [ECMA-402 Intl.DateTimeFormat 的选项](http://www.ecma-international.org/ecma-402/2.0/#sec-intl-datetimeformat-constructor)。 之后就像语言环境信息一样,你需要指定 `VueI18n` 构造函数的 `dateTimeFormats` 选项: ```js const i18n = new VueI18n({ dateTimeFormats }) new Vue({ i18n }).$mount('#app') ``` 模板如下: ```html

{{ $d(new Date(), 'short') }}

{{ $d(new Date(), 'long', 'ja-JP') }}

``` 输出如下: ```html

Jan 18, 2021

2021年1月18日日曜日 午前5:47

``` ================================================ FILE: vuepress/zh/guide/directive.md ================================================ # 自定义指令本地化 :::tip 支持的版本 :new: 7.3+ 新增 ::: 你不仅可以使用 `$t` 方法进行翻译,还可以使用 `v-t` 自定义指令。 ## 字符串语法 你可以使用字符串语法传递语言环境信息的键名路径。 JavaScript: ```js new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { hello: 'hi there!' }, ja: { hello: 'こんにちは!' } } }), data: { path: 'hello' } }).$mount('#string-syntax') ``` 模板: ```html

``` 输出: ```html

hi there!

hi there!

``` ## 对象语法 你可以使用对象语法。 Javascript: ```js new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { hello: 'hi {name}!' }, ja: { hello: 'こんにちは、{name}!' } } }), computed: { nickName () { return 'kazupon' } }, data: { path: 'hello' } }).$mount('#object-syntax') ``` 模板: ```html

``` 输出: ```html

こんにちは、kazupon!

hi kazupon!

``` ## 使用翻译 :::tip 支持版本 :new: 8.7+ 新增 ::: 当 `v-t` 指令应用于 [`` 组件](https://cn.vuejs.org/v2/api/#transition)内的元素时,你可能会注意到过渡动画之后的翻译过的信息会消失。这与 `` 组件实现的方式有关——**在过渡开始之前** ,`` 组件内消失元素中的所有指令都将被销毁。此行为可能导致内容在短过渡时闪烁,但在长过渡时最明显。 为了确保在转换期间指令内容不会被触及,只需将[`.preserve` 修饰符](../api/#v-t)添加到 `v-t` 指令定义中。 Javascript: ```js new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { preserve: 'with preserve' }, } }), data: { toggle: true } }).$mount('#in-transitions') ``` 模板: ```html
``` 也可以在 `VueI18n` 实例本身设置全局设置,这将对没有修饰符的所有 `v-t` 指令产生影响。 Javascript: ```js new Vue({ i18n: new VueI18n({ locale: 'en', messages: { en: { preserve: 'with preserve' }, }, preserveDirectiveContent: true }), data: { toggle: true } }).$mount('#in-transitions') ``` 模板: ```html
``` 关于上面的例子,请参阅[示例](https://github.com/kazupon/vue-i18n/tree/dev/examples/directive) ## `$t` vs `v-t` ### `$t` `$t` 是扩展的 Vue 实例方法, 它有以下优点和缺点: #### 优点 你可以**灵活地**在模板以及 Vue 实例的计算属性和方法中使用 mustash 语法 `{{}}`。 #### 缺点 `$t` 在**每次**重新渲染时都会被执行,因此它确实有翻译成本。 ### `v-t` `v-t` 是一个自定义指令,它有以下优点和缺点: #### 优点 `v-t` 比 `$t` 方法具有**更好的**性能,因为在一次翻译时自定义指令会进行缓存。此外可以使用由 [`vue-i18n-extensions`](https://github.com/kazupon/vue-i18n-extensions) 提供的 Vue 编译器模块进行预翻译。 因此,可以进行**更多性能优化**。 #### 缺点 `v-t` 不能像 `$t` 一样灵活使用,它更**复杂**。带有 `v-t` 的翻译内容会被插入到元素的 `textContent` 中。此外,当你使用服务器渲染时,你需要设置[自定义指令](https://github.com/kazupon/vue-i18n-extensions#directive-v-t-custom-directive-for-server-side)到 `createRenderer` 函数的 `directives` 选项。 ================================================ FILE: vuepress/zh/guide/fallback.md ================================================ # 回退本地化 *总结:使用`fallbackLocale:''`选择首选语言缺少翻译时要使用的语言。* ## 使用语言环境的隐式回退 如果给出的 `locale` 包含领土和可选的方言,则隐式回退将自动激活。 例如,对于 `de-DE-bavarian`,以下内容将被视为备用: 1. `de-DE-Bavarian` 2. `de-DE` 3. `de` 要禁止自动回退,请添加后缀感叹号 `!`,例如 `de-DE!`。 # 具有一个语言环境的显式回退 以下语言环境信息的 `ja` 语言环境中不存在 `message` 键: ```js const messages = { en: { message: 'hello world' }, ja: { // 没有翻译的本地化 `hello` } } ``` 当为 VueI18n 构造函数选项指定 `fallbackLocale` 选项时,`message` 键使用 `en` 语言环境进行本地化: ```js const i18n = new VueI18n({ locale: 'ja', fallbackLocale: 'en', messages }) ``` 模板如下: ```html

{{ $t('message') }}

``` 输出如下: ```html

hello world

``` 注意,默认情况下回退到 `fallbackLocale` 会产生两个控制台警告: ``` [vue-i18n] Value of key 'message' is not a string! [vue-i18n] Fall back to translate the keypath 'message' with 'en' locale. ``` 为了避免这些警告 (同时保留那些完全没有翻译给定关键字的警告),需初始化 `VueI18n` 实例时设置 `silentFallbackWarn:true`。 ## 回退插值 由于翻译的键值是字符串,因此也可以作为翻译的值: ```javascript const messages = { ja: { 'Hello world': 'こんにちは、世界' } } ``` 这是一种很自然的书写方式,如果在`message`中找不到相应的键值将回退到原本的语言: *注意: `fallbackRoot`的优先级高于`formatFallbackMessages`* ```html

{{ $t('Hello world') }}

``` 为了实现此功能,可以通过设置`formatFallbackMessages`为`true`: ```javascript const messages = { ru: { 'Hello {name}': 'Здравствуйте {name}' } } const i18n = new VueI18n({ locale: 'ru', fallbackLocale: 'en', formatFallbackMessages: true, messages }) ``` 模板如下: ```html

{{ $t('Hello {name}', { name: 'John' }}) }}

{{ $t('The weather today is {condition}!', { condition: 'sunny' }) }}

``` 将会输出: ```html

Здравствуйте John

The weather today is sunny!

``` ================================================ FILE: vuepress/zh/guide/formatting.md ================================================ # 格式化 ## 具名格式 语言环境信息如下: ```js const messages = { en: { message: { hello: '{msg} world' } } } ``` 模板如下: ```html

{{ $t('message.hello', { msg: 'hello' }) }}

``` 输出如下: ```html

hello world

``` ## 列表格式 语言环境信息如下: ```js const messages = { en: { message: { hello: '{0} world' } } } ``` 模板如下: ```html

{{ $t('message.hello', ['hello']) }}

``` 输出如下: ```html

hello world

``` 列表格式也接受类似数组的对象: ```html

{{ $t('message.hello', {'0': 'hello'}) }}

``` 输出如下: ```html

hello world

``` ## HTML 格式化 :::warning 提示 :warning: 在你的网站上动态插入任意 HTML 可能非常危险,因为它很容易导致 XSS 攻击。仅对可信内容使用 HTML 插值,而不对用户提供的内容使用。 我们建议使用[组件插值](interpolation.md) 功能。 ::: 在某些情况下,你可能希望将翻译呈现为 HTML 信息而不是静态字符串。 ```js const messages = { en: { message: { hello: 'hello
world' } } } ``` 模板如下: ```html

``` 输出如下 (取代预先格式化的信息) ```html

hello world

``` ## 支持 ruby on rails 的 i18n 格式 语言环境信息如下: ```js const messages = { en: { message: { hello: '%{msg} world' } } } ``` 模板如下: ```html

{{ $t('message.hello', { msg: 'hello' }) }}

``` 输出如下: ```html

hello world

``` ## 自定义格式 有时,你可能需要使用自定义格式进行翻译 (例如:[ICU 信息语法](http://userguide.icu-project.org/formatparse/messages))。 你可以使用实现[格式化接口](https://github.com/kazupon/vue-i18n/blob/dev/decls/i18n.js#L41-L43) 的自定义格式化函数来实现。 以下使用 ES2015 class 语法的自定义格式化函数: ```js // 实现自定义格式 class CustomFormatter { constructor (options) { // ... } // // 插值 // // @param {string} 信息 // 列表或具名格式的字符串。 // 例如: // - 具名格式:'Hi {name}' // - 列表格式:'Hi {0}' // // @param {Object | Array} 值 // `message` 插值的值 // 使用 `$t`, `$tc` 和 `i18n` 函数式组件传递值。 // e.g. // - $t('hello', { name: 'kazupon' }) -> 传递值:Object `{ name: 'kazupon' }` // - $t('hello', ['kazupon']) -> 传递值:Array `['kazupon']` // - `i18n` 函数式组件 (组件插值) // //

kazupon

//

how are you?

//
// -> 传递值:Array (included VNode): // `[VNode{ tag: 'p', text: 'kazupon', ...}, VNode{ tag: 'p', text: 'how are you?', ...}]` // // @return {Array} // 插值,你需要返回以下内容: // - 当使用 `$t` 或 `$tc` 数组中应该是字符串。 // - 当使用 `i18n` 函数式组件时 数组中应包含 VNode 对象。 // interpolate (message, values) { // 在这里实现插值逻辑 // ... // 返回插值数组 return ['resolved message string'] } } // 注册 `formatter` 选项 const i18n = new VueI18n({ locale: 'en-US', formatter: new CustomFormatter(/* 这里是构造函数选项 */), messages: { 'en-US': { // ... }, // ... } }) // 启动! new Vue({ i18n }).$mount('#app') ``` 你可以查看[自定义格式化函数的官方示例](https://github.com/kazupon/vue-i18n/tree/dev/examples/formatting/custom)。 ================================================ FILE: vuepress/zh/guide/hot-reload.md ================================================ # 热重载 您可以使用Webpack的 [Hot Module Replacement](https://webpack.js.org/concepts/hot-module-replacement/) (HMR) 功能来监视本地化文件中的更改以及将热更改重新加载到您的应用程序中。 你可以监视本地化文件中的更改,并将更改热重载到应用程序中。 ## 基本例子 如果仅使用静态语言环境集,则可以显式热加载这些语言环境: ```js import Vue from "vue" import VueI18n from "vue-i18n" import en from './en' import ja from './ja' // 语言环境信息 const messages = { en, ja } // VueI18n 实例 const i18n = new VueI18n({ locale: 'en', messages }) // 运行程序 const app = new Vue({ i18n, // ... }).$mount('#app') // 热更新 if (module.hot) { module.hot.accept(['./en', './ja'], function () { i18n.setLocaleMessage('en', require('./en').default) i18n.setLocaleMessage('ja', require('./ja').default) // 同样可以通过 $i18n 属性进行热更新 // app.$i18n.setLocaleMessage('en', require('./en').default) // app.$i18n.setLocaleMessage('ja', require('./ja').default) }) } ``` ## 进阶范例 如果您想支持一组不断变化的语言环境,则可以使用 `require.context` 动态地重新加载这些语言环境: ```js import Vue from "vue"; import VueI18n from "vue-i18n"; Vue.use(VueI18n); // 加载所有语言环境并记住上下文 function loadMessages() { const context = require.context("./locales", true, /[a-z0-9-_]+\.json$/i); const messages = context .keys() .map((key) => ({ key, locale: key.match(/[a-z0-9-_]+/i)[0] })) .reduce( (messages, { key, locale }) => ({ ...messages, [locale]: context(key), }), {} ); return { context, messages }; } const { context, messages } = loadMessages(); // VueI18n 实例 const i18n = new VueI18n({ locale: "en", messages, }); // 运行程序 const app = new Vue({ i18n, // ... }).$mount('#app'); // 热更新 if (module.hot) { module.hot.accept(context.id, () => { const { messages: newMessages } = loadMessages(); Object.keys(newMessages) .filter((locale) => messages[locale] !== newMessages[locale]) .forEach((locale) => { messages[locale] = newMessages[locale]; i18n.setLocaleMessage(locale, messages[locale]); }); }); } ``` ================================================ FILE: vuepress/zh/guide/interpolation.md ================================================ # 组件插值 ## 基本用法 :::tip 支持版本 :new: 7.0+ 新增 ::: 有时,我们需要使用包含 HTML 标签或组件的语言环境信息进行本地化。例如: ```html

I accept xxx Terms of Service Agreement

``` 在上面的信息中,如果你使用 `$t`,可能你会尝试编写以下语言环境信息: ```js const messages = { en: { term1: 'I Accept xxx\'s', term2: 'Terms of Service Agreement' } } ``` 你可能会尝试在以下模板中实现: ```html

{{ $t('term1') }}{{ $t('term2') }}

``` 输出: ```html

I accept xxx Terms of Service Agreement

``` 这是非常麻烦的,如果在语言环境信息中配置 `` 标签,则可能由于使用了 `v-html="$t('term')"` 进行本地化而存在被 XSS 攻击的可能性。 你可以使用 `i18n` 函数式组件来避免它。例如: ```html ``` ```js const messages = { en: { tos: 'Term of Service', term: 'I accept xxx {0}.' }, ja: { tos: '利用規約', term: '私は xxx の{0}に同意します。' } } const i18n = new VueI18n({ locale: 'en', messages }) new Vue({ i18n, data: { url: '/term' } }).$mount('#app') ``` 输出如下: ```html
``` 关于上面的例子,见[示例](https://github.com/kazupon/vue-i18n/tree/dev/examples/interpolation) `i18n` 函数式组件的子元素用 `path` 属性的语言环境信息进行插值。 在上面的例子中: :::v-pre `{{ $t('tos') }}` ::: 被插入了语言环境信息 `term`。 在上面的示例中,组件插值遵循**列表格式**。`i18n` 函数式组件的子项按其出现顺序进行插值。 ## 高级用法 :::危险提示!! 在下一个主要版本中,`place` 和 `places` 属性将被弃用。 请切换到插槽语法。 ::: :::tip 支持版本 :new: 7.2+ 新增 ::: :::warning 提示 :warning: 在 `i18n` 组件中,仅包含空格的文本内容将被省略。 ::: 在 `place` 特性的帮助下支持具名格式。例如: ```html
{{ changeLimit }} {{ $t('change') }}
``` ```js const messages = { en: { info: 'You can {action} until {limit} minutes from departure.', change: 'change your flight', refund: 'refund the ticket' } } const i18n = new VueI18n({ locale: 'en', messages }) new Vue({ i18n, data: { changeUrl: '/change', refundUrl: '/refund', changeLimit: 15, refundLimit: 30 } }).$mount('#app') ``` 输出: ```html

You can change your flight until 15 minutes from departure.

``` :::warning 提示 :warning: `i18n` 组件的所有子项都必须设置 `place` 属性。否则它将回退到列表格式。 ::: 如果你仍想在命名格式中插入文本内容,可以在 `i18n` 组件上定义 `places` 属性。例如: ```html ``` 输出: ```html

You can refund your ticket until 30 minutes from departure.

``` ================================================ FILE: vuepress/zh/guide/lazy-loading.md ================================================ # 延迟加载翻译 一次加载所有翻译文件是过度和不必要的。 使用 Webpack 时,延迟加载或异步加载转换文件非常简单。 让我们假设我们有一个类似于下面的项目目录 ``` our-cool-project -dist -src --routes --store --setup ---i18n-setup.js --lang ---en.js ---it.js ``` `lang` 文件夹是我们所有翻译文件所在的位置。`setup` 文件夹是我们的任意设置的文件,如 i18n-setup,全局组件 inits,插件 inits 和其他位置。 ```js //i18n-setup.js import Vue from 'vue' import VueI18n from 'vue-i18n' import messages from '@/lang/en' import axios from 'axios' Vue.use(VueI18n) export const i18n = new VueI18n({ locale: 'en', // 设置语言环境 fallbackLocale: 'en', messages // 设置语言环境信息 }) const loadedLanguages = ['en'] // 我们的预装默认语言 function setI18nLanguage (lang) { i18n.locale = lang axios.defaults.headers.common['Accept-Language'] = lang document.querySelector('html').setAttribute('lang', lang) return lang } export function loadLanguageAsync(lang) { // 如果语言相同 if (i18n.locale === lang) { return Promise.resolve(setI18nLanguage(lang)) } // 如果语言已经加载 if (loadedLanguages.includes(lang)) { return Promise.resolve(setI18nLanguage(lang)) } // 如果尚未加载语言 return import(/* webpackChunkName: "lang-[request]" */ `@/i18n/messages/${lang}.js`).then( messages => { i18n.setLocaleMessage(lang, messages.default) loadedLanguages.push(lang) return setI18nLanguage(lang) } ) } ``` 简而言之,我们正在创建一个新的 VueI18n 实例。然后我们创建一个 `loadedLanguages` 数组,它将跟踪我们加载的语言。接下来是 `setI18nLanguage` 函数,它将实际更改 vueI18n 实例、axios 以及其它需要本地化的地方。 `loadLanguageAsync` 是实际用于更改语言的函数。加载新文件是通过import功能完成的,`import` 功能由 Webpack 慷慨提供,它允许我们动态加载文件,并且因为它使用 promise,我们可以轻松地等待加载完成。 你可以在 [Webpack 文档](https://webpack.js.org/guides/code-splitting/#dynamic-imports) 中了解有关导入功能的更多信息。 使用 `loadLanguageAsync` 函数很简单。一个常见的用例是在 vue-router beforeEach 钩子里面。 ```js router.beforeEach((to, from, next) => { const lang = to.params.lang loadLanguageAsync(lang).then(() => next()) }) ``` 我们可以通过检查 `lang` 实际上是否支持来改进这一点,调用 `reject` 这样我们就可以在 beforeEach 捕获路由转换。 ================================================ FILE: vuepress/zh/guide/locale.md ================================================ # 语言环境变更 通常,使用 Vue 根实例作为起点,使用 `VueI18n` 类的 `locale` 属性作为参考来本地化所有子组件。 有时你可能希望动态更改语言环境。在这种情况下,你可以更改 `VueI18n` 实例的 `locale` 属性的值。 ```js const i18n = new VueI18n({ locale: 'ja', // 设置语言环境 ... }) // 创建 Vue 根实例 new Vue({ i18n, ... }).$mount('#app') // 更改为其它的 locale i18n.locale = 'en' ``` 每个组件都包含一个引用为 `$i18n` 属性的 `VueI18n` 实例,该实例也可用于更改语言环境。 示例: ```vue ``` :::warning 警告 :warning: 对于使用了 `sync: false` 的组件,语言环境的更改将被忽略。 ::: :::warning 组件与根范围 :warning: 在组件内更改 `$i18n.locale` 不会更新根语言环境。 如果您依靠根语言环境,例如在使用 [root fallbacks](./fallback.html) 时,请使用 `$root.$i18n.locale` 而不是$ `i18n.locale`。 ::: ================================================ FILE: vuepress/zh/guide/messages.md ================================================ # 语言环境信息的语法 ## 结构 语言环境信息的语法如下: ```typescript // 作为 Flowtype 定义,语言环境信息的语法类似于 BNF 注释 type LocaleMessages = { [key: Locale]: LocaleMessageObject }; type LocaleMessageObject = { [key: Path]: LocaleMessage }; type LocaleMessageArray = LocaleMessage[]; type MessageContext = { list: (index: number) => mixed, named: (key: string) => mixed, linked: (key: string) => TranslateResult, values: any, path: string, formatter: Formatter, messages: LocaleMessages, locale: Locale }; type MessageFunction = (ctx: MessageContext) => string; type LocaleMessage = string | MessageFunction | LocaleMessageObject | LocaleMessageArray; type Locale = string; type Path = string; ``` 基于以上语法,你可以配置以下结构的 Locale 信息: ```json { "en": { // 'en' Locale "key1": "this is message1", // 基本的 "nested": { // 嵌套 "message1": "this is nested message1" }, "errors": [ // 数组 "this is 0 error code message", { // 数组嵌套对象 "internal1": "this is internal 1 error message" }, [ // 数组嵌套数组 "this is nested array error 1" ] ] }, "ja": { // 'ja' Locale // ... } } ``` 在上面的语言环境信息的结构中,你可以使用以下键名路径进行翻译。 ```html

{{ $t('key1') }}

{{ $t('nested.message1') }}

{{ $t('errors[0]') }}

{{ $t('errors[1].internal1') }}

{{ $t('errors[2][0]') }}

``` 输出以下内容: ```html

this is message1

this is nested message1

this is 0 error code message

this is internal 1 error message

this is nested array error 1

``` ## Linked locale messages 如果有一个翻译关键字总是与另一个具有相同的具体文本,你可以链接到它。要链接到另一个翻译关键字,你所要做的就是在其内容前加上一个 `@:` 符号后跟完整的翻译键名,包括你要链接到的命名空间。 语言环境信息如下: ```js const messages = { en: { message: { the_world: 'the world', dio: 'DIO:', linked: '@:message.dio @:message.the_world !!!!' } } } ``` 模板如下: ```html

{{ $t('message.linked') }}

``` 输出如下: ```html

DIO: the world !!!!

``` ### 格式化链接的语言环境消息 如果语言区分字符大小写,则可能需要控制链接的语言环境消息的大小写。 链接的消息可以用修饰符 `@.modifier:key` 格式化。 以下修饰符当前可用。 * `upper`: 链接消息中的所有字符均大写 * `lower`: 小写链接消息中的所有字符 * `capitalize`: 大写链接消息中的第一个字符 语言环境消息如下: ```javascript const messages = { en: { message: { homeAddress: 'Home address', missingHomeAddress: 'Please provide @.lower:message.homeAddress' } } } ``` ```html

{{ $t('message.missingHomeAddress') }}

``` 输出以下内容: ```html

Please provide home address

``` 您可以添加修饰符或覆盖将 `modifiers` 选项传递给 `VueI18n` 构造函数的现有修饰符。 ```javascript const i18n = new VueI18n({ locale: 'en', modifiers: { snakeCase: (str) => str.split(' ').join('-') }, messages: { // ... }, }) ``` ### 按括号分组 链接到的语言环境信息的键名也可以形如 `@:(message.foo.bar.baz)`,其中链接到另一段翻译的键名在括号 `()` 里。 如果链接 `@:message.something` 后紧跟着一个点 `.`,则此选项非常有用,因为它本不该成为但却成为了链接的一部分。 语言环境信息如下: ```js const messages = { en: { message: { dio: 'DIO', linked: 'There\'s a reason, you lost, @:(message.dio).' } } } ``` 模板如下: ```html

{{ $t('message.linked') }}

``` 输出如下: ```html

There's a reason, you lost, DIO.

``` ## 留言功能 vue-i18n 建议在翻译消息时使用基于列表的字符串或命名格式作为语言环境消息。 但是,在某些情况下,由于复杂的语言语法,您确实需要JavaScript的全部编程功能。 因此,您可以使用 **message function** 来代替基于字符串的消息。 以下是一个返回简单问候语的消息函数: ```js const messages = { en: { greeting: (ctx) => 'hello!' } } ``` 使用消息功能非常容易! 您只需使用 `$t` 或 `t` 指定消息功能的键: ```html

{{ $t('greeting') }}

``` 输出如下: ```html

hello!

``` 消息功能输出消息,该消息具有消息功能的返回值。 ### 命名格式 vue-i18n 支持[命名格式](./formatting.md#named-formatting) 作为基于字符串的消息格式。 vue-i18n用$ t或t插值参数值,并可以将其输出。 使用 **消息上下文** 的消息功能可以完成以下操作: 这是问候的示例: ```js const messages = { en: { greeting: (ctx) => `hello, ${ctx.named('name')}!` } } ``` 模板: ```html

{{ $t('greeting', { name: 'DIO' }) }}

``` 输出如下: ```html

hello, DIO!

``` 消息上下文具有命名函数。 您需要指定键来解析以 `$t` 或 `t` 命名的值。 ### 清单格式 列表格式的使用类似于上述命名格式。 vue-i18n 支持 [列表格式](./formatting.md#list-formatting) 作为基于字符串的消息格式。 vue-i18n用$ t或t插值参数值,并可以将其输出。 您可以通过使用消息上下文对消息函数执行相同的操作: 这是问候的示例: ```js const messages = { en: { greeting: (ctx) => `hello, ${ctx.list(0)}!` } } ``` 模板: ```html

{{ $t('greeting', ['DIO']) }}

``` 输出如下: ```html

hello, DIO!

``` 消息上下文具有列表功能。 您需要指定索引来解析由 `$t` 或 `t` 列表指定的值。 ### 局限性 在消息功能中,以下基于字符串提供的功能无法通过消息上下文使用: - 链接的区域设置消息 - 复数 ================================================ FILE: vuepress/zh/guide/number.md ================================================ # 数字本地化 :::tip 支持版本 :new: 7.0+ 新增 ::: 你可以使用你定义的格式来本地化数字。 数字格式如下: ```js const numberFormats = { 'en-US': { currency: { style: 'currency', currency: 'USD' } }, 'ja-JP': { currency: { style: 'currency', currency: 'JPY', currencyDisplay: 'symbol' } } } ``` 如上,你可以指定具名的 (例如:`currency` 等) 的数字格式,并且需要使用 [ECMA-402 Intl.NumberFormat 的选项](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat)。 之后就像语言环境信息一样,你需要指定 `VueI18n` 构造函数的 `numberFormats` 选项: ```js const i18n = new VueI18n({ numberFormats }) new Vue({ i18n }).$mount('#app') ``` 模板如下: ```html

{{ $n(100, 'currency') }}

{{ $n(100, 'currency', 'ja-JP') }}

``` 输出如下: ```html

$100.00

¥100

``` ## 自定义格式 :::tip 支持版本 :new: 8.10+ 新增 ::: `$n` 方法返回的结果字符串带有完全格式化的数字,该数字只能作为整体使用。 在需要格式化格式化数字的某些部分(例如小数位)的情况下,`$n` 是不够的。 在这种情况下,`` 功能组件将有所帮助。 有了最少的一组属性,`` 产生的输出与 `$n` 相同,并包装到已配置的DOM元素中。 以下模板: ```html
``` 将产生以下输出: ```html
100 $100.00 ¥100
``` 但是,当与[范围内的插槽](https://vuejs.org/v2/guide/components-slots.html#Scoped-Slots) 一起使用时,该组件的真正功能就会发挥作用。 假设需要用较粗的字体呈现数字的整数部分。 这可以通过指定 `integer` 作用域的插槽元素来实现: ```html {{ slotProps.integer }} ``` 上面的模板将产生以下 HTML: ```html $100.00 ``` 可以同时指定多个作用域插槽: ```html {{ slotProps.currency }} {{ slotProps.integer }} {{ slotProps.group }} {{ slotProps.fraction }} ``` (此结果 HTML 进行了格式化,以提高可读性) ```html 1 , 234 00 ``` 您可以通过指定 `tag` 属性来选择根容器的节点类型。 如果省略,则默认为 `'span'`。 您也可以将其设置为布尔值 `false` 以直接插入子节点,而无需创建根元素。 可以在 [API 页面](../api/readme.md#i18n-n-functional-component) 中找到受支持的作用域插槽以及其他 `` 属性的完整列表。 ================================================ FILE: vuepress/zh/guide/pluralization.md ================================================ # 复数 你可以使用复数进行翻译。你必须定义具有管道 `|` 分隔符的语言环境,并在管道分隔符中定义复数。 *您的模板将需要使用 `$tc()` 而不是 `$t()`。 语言环境信息如下: ```js const messages = { en: { car: 'car | cars', apple: 'no apples | one apple | {count} apples' } } ``` 模板如下: ```html

{{ $tc('car', 1) }}

{{ $tc('car', 2) }}

{{ $tc('apple', 0) }}

{{ $tc('apple', 1) }}

{{ $tc('apple', 10, { count: 10 }) }}

``` 输出如下: ```html

car

cars

no apples

one apple

10 apples

``` ## 通过预定义的参数访问该数字 你无需明确指定复数的数字。可以通过预定义的命名参数 `{count}` 和/或 `{n}` 在语言环境信息中访问该数字。如有必要,你可以覆盖这些预定义的命名参数。 语言环境信息如下: ```js const messages = { en: { apple: 'no apples | one apple | {count} apples', banana: 'no bananas | {n} banana | {n} bananas' } } ``` 模板如下: ```html

{{ $tc('apple', 10, { count: 10 }) }}

{{ $tc('apple', 10) }}

{{ $tc('banana', 1, { n: 1 }) }}

{{ $tc('banana', 1) }}

{{ $tc('banana', 100, { n: 'too many' }) }}

``` 输出如下: ```html

10 apples

10 apples

1 banana

1 banana

too many bananas

``` ## 自定义复数 但是,这种多元化并不适用于所有语言(例如,斯拉夫语言具有不同的多元化规则)。 为了实现这些规则,您可以将可选的 `pluralizationRules` 对象传递给`VueI18n` 构造函数选项。 使用针对斯拉夫语言(俄语,乌克兰语等)的规则的非常简化的示例: ```js new VueI18n({ // Key - 在这种情况下,用于规则 `'ru'` 的语言 // Value - 选择正确的复数形式的功能 pluralizationRules: { /** * @param choice {number} 输入给$的选择索引 $tc:`$tc('path.to.rule', choiceIndex)` * @param choicesLength {number} 可用选择总数 * @returns 最终选择索引以选择复数单词 */ 'ru': function(choice, choicesLength) { // this === VueI18n 实例,因此本地属性也存在于此 if (choice === 0) { return 0; } const teen = choice > 10 && choice < 20; const endsWithOne = choice % 10 === 1; if (choicesLength < 4) { return (!teen && endsWithOne) ? 1 : 2; } if (!teen && endsWithOne) { return 1; } if (!teen && choice % 10 >= 2 && choice % 10 <= 4) { return 2; } return (choicesLength < 4) ? 2 : 3; } } }) ``` 这将有效地实现以下目的: ```javascript const messages = { ru: { car: '0 машин | {n} машина | {n} машины | {n} машин', banana: 'нет бананов | {n} банан | {n} банана | {n} бананов' } } ``` 格式在哪里 `0 东西 | 事情以结尾结束 1 | 事情以结尾结束 2-4 | 事情以结尾结束 5-9, 0 和青少年 (10-19)`. 附言 斯拉夫多元化是困难的,您可以阅读有关它的更多信息 [这里](http://www.russianlessons.net/lessons/lesson11_main.php). 你的模板仍然需要使用 `$tc()`,而不是 `$t()` : ```html

{{ $tc('car', 1) }}

{{ $tc('car', 2) }}

{{ $tc('car', 4) }}

{{ $tc('car', 12) }}

{{ $tc('car', 21) }}

{{ $tc('banana', 0) }}

{{ $tc('banana', 4) }}

{{ $tc('banana', 11) }}

{{ $tc('banana', 31) }}

``` 结果如下: ```html

1 машина

2 машины

4 машины

12 машин

21 машина

нет бананов

4 банана

11 бананов

31 банан

``` ### 默认多元 如果在多元化地图中找不到您当前的语言环境,则将使用英语的 [默认](#复数) 规则。 ================================================ FILE: vuepress/zh/guide/sfc.md ================================================ # 单文件组件 ## 基本用法 如果使用单文件组件构建 Vue 组件或 Vue 应用程序,则可以管理 `i18n` 自定义块的语言环境信息。 以下是[单文件组件示例](https://github.com/kazupon/vue-i18n/tree/dev/examples/sfc): ```vue { "en": { "hello": "hello world!" }, "ja": { "hello": "こんにちは、世界!" } } ``` ## 安装 vue-i18n-loader 为了使用 `` 自定义块,你需要安装 `vue-loader` 和 `vue-i18n-loader`。如果你使用了单文件组件,[vue-loader](https://github.com/vuejs/vue-loader) 很可能已在项目中使用了,那么 [vue-i18n-loader](https://github.com/kazupon/vue-i18n-loader) 必须另外安装: ```sh npm i --save-dev @kazupon/vue-i18n-loader ``` ## Webpack 需要对 Webpack 进行以下配置: 对于 vue-loader v15 或更高版本: ```js module.exports = { // ... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', }, { resourceQuery: /blockType=i18n/, type: 'javascript/auto', loader: '@kazupon/vue-i18n-loader' } // ... ] }, // ... } ``` 对于 vue-loader v14: ```js module.exports = { // ... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { loaders: { // 你需要指定 `i18n` 的值为 `vue-i18n-loader` i18n: '@kazupon/vue-i18n-loader' } } }, // ... ] }, // ... } ``` ## Vue CLI 3.0 [Vue CLI 3.0](https://github.com/vuejs/vue-cli) 隐藏了 webpack 配置,因此,如果我们想在单文件组件中添加对 `` 标记的支持,我们需要修改现有配置。 为此,我们必须在项目的根目录下创建一个 `vue.config.js`。完成后,我们必须包括以下内容: 对于 vue-loader v15 或更高版本: ```js module.exports = { chainWebpack: config => { config.module .rule("i18n") .resourceQuery(/blockType=i18n/) .type('javascript/auto') .use("i18n") .loader("@kazupon/vue-i18n-loader") .end(); } } ``` 对于 vue-loader v14: ```js const merge = require('deepmerge') module.exports = { chainWebpack: config => { config.module .rule('vue') .use('vue-loader') .tap(options => merge(options, { loaders: { i18n: '@kazupon/vue-i18n-loader' } }) ) } } ``` _别忘了安装[deepmerge](https://github.com/KyleAMathews/deepmerge)! (`npm i deepmerge -D` 或 `yarn add deepmerge -D`)_ 如果你想了解有关修改现有配置的更多信息[点击这里](https://cli.vuejs.org/guide/webpack.html)。 ## Laravel-Mix 对于带有 vue-loader v15 或更高版本的 Laravel-mix 4: ```js // 使用 “i18n” 方法扩展 Mix ,加载 vue-i18n-loader mix.extend( 'i18n', new class { webpackRules() { return [ { resourceQuery: /blockType=i18n/, type: 'javascript/auto', loader: '@kazupon/vue-i18n-loader', }, ]; } }(), ); // 确保在 `.js(..., ...)` 之前调用 `.i18n()` (来加载加载器) mix.i18n() .js( 'resources/js/App.js', 'public/js/app.js' ) ... ``` 对于带有 vue-loader v14 的 Laravel-mix 2: 从 Laravel-mix 的 [V2.1](https://github.com/JeffreyWay/laravel-mix/releases/tag/v2.1) 开始,你可以通过 `mix.extend()` 添加自定义规则。Laravel mix 已经有了处理 .vue 文件的规则。要添加 `vue-i18n-loader`,请将以下内容添加到 `webpack.mix.js`: ```js // 下面的代码将注入 i18n Kazupon/vue-18-loader 作为 .vue 文件的加载器。 mix.extend( 'i18n', function( webpackConfig, ...args ) { webpackConfig.module.rules.forEach( ( module ) => { // 搜索处理 .vue 文件的 “vue-loader” 组件。 if( module.loader !== 'vue-loader' ) { return; } // 在此模块中,为 i18n 标记添加 vue-i18n-loader。 module.options.loaders.i18n = '@kazupon/vue-i18n-loader'; } ); } ); // 确保在 `.js(...,...)` 之前调用 `.i18n()` mix.i18n() .js( 'resources/assets/js/App.js', 'public/js/app.js' ) ... ``` ## 加载 YAML `i18n` 自定义块需要指定为 JSON 格式,你也可以通过使用 `vue-loader` 预加载器功能来使用 `YAML` 格式。 以下是 `YAML` 格式的 `i18n` 自定义块: ```vue en: hello: "hello world!" ja: hello: "こんにちは、世界!" ``` Webpack 配置如下: 对于 vue-loader v15 或更高版本: ```js // Vue CLI 3.0 module.exports = { chainWebpack: config => { config.module .rule("i18n") .resourceQuery(/blockType=i18n/) .type('javascript/auto') .use("i18n") .loader("@kazupon/vue-i18n-loader") .end() .use('yaml') .loader('yaml-loader') .end() } } ``` 对于 vue-loader v14: ```js module.exports = { // ... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { preLoaders: { i18n: 'yaml-loader' }, loaders: { i18n: '@kazupon/vue-i18n-loader' } } }, // ... ] }, // ... } ``` ## 多个自定义块 你可以使用具有多个 `i18n` 自定义块的语言环境信息。 ```vue { "en": { "hello": "hello world!" }, "ja": { "hello": "こんにちは、世界!" } } ``` 如上所见,第一个自定义块使用 `src` 特性加载常用的语言环境信息,第二个自定义块加载仅在该单文件组件中定义的语言环境信息。这些语言环境信息将合并为组件的语言环境信息。 这样,多个自定义块在想要用作模块时非常有用。 ## Scoped 风格 当使用带有 `scoped style` `vue-i18n` 时,重要的是要记住使用[深度选择器](https://vue-loader.vuejs.org/zh/guide/scoped-css.html#深度作用选择器) 来设置嵌套转换的样式。例如: __翻译仅包含文本__(不使用深层选择器) ```vue { "en": { "hello": "hello world!" }, "ja": { "hello": "こんにちは、世界" } } ``` __使用 HTML 元素翻译__(必须使用深度选择器) ```vue { "en": { "hello": "helloworld!" }, "ja": { "hello": "こんにちは、世界!" } } ``` ## 函数式组件中的自定义块 如果单个文件组件具有使用函数式组件的模板,并且你已经定义了 `i18n` 自定义块,请注意你无法使用语言环境信息进行本地化。 例如,以下代码无法使用 `i18n` 自定义块的语言环境信息进行本地化。 ```vue { "en": { "hello": "hello world" }, "ja": { "hello": "こんにちは、世界" } } ``` ================================================ FILE: vuepress/zh/guide/tooling.md ================================================ # 工具 为了支持开发,我们官方提供了一些工具。 此外,还有第三方供应商提供的集成了 Vue I18n 的工具。 ## 官方工具 ### Vue Cli 插件 [vue-cli-plugin-i18n](https://github.com/kazupon/vue-cli-plugin-i18n) 是官方提供的 Vue Cli 插件。 使用此插件,您可以为Vue应用程序设置 i18n 环境,并支持 i18n 开发环境。 ### Webpack Loader [vue-i18n-loader](https://github.com/kazupon/vue-i18n-loader) 是官方提供的 Webpack Loader。 使用此加载程序,您可以在单个文件组件中使用 `i18n` 自定义块。 关于 `i18n` 自定义块,请参见 [单文件组件](./sfc.md) ### ESLint 插件 [eslint-plugin-vue-i18n](https://intlify.github.io/eslint-plugin-vue-i18n/) 是为 Vue I18n 编写的 ESLint 插件。 它可以轻松地将一些本地化 lint 功能集成到 Vue.js 应用程序中。 ### Extensions 在[vue-i18n-extensions](https://github.com/kazupon/vue-i18n-extensions) 你可以找到为 Vue I18n 编写的插件。 您可以使用此扩展来启用 SSR 并提高 Vue I18n 的性能。 ## 第三方工具 ### BabelEdit [BabelEdit](https://www.codeandweb.com/babeledit) 是 Web 应用程序的翻译编辑器。 BabelEdit可以翻译 `json` 文件,也可以翻译单文件组件的 `i18n` 自定义块。 欲了解更多,请看[教程](https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-vue-app-with-vue-i18n) ### i18n Ally [i18n Ally](https://marketplace.visualstudio.com/items?itemName=antfu.i18n-ally) 是 VSCode 的 i18n 扩展。 i18n Ally 为您的 i18n 开发提供了出色的 DX。 在[自述文件](https://github.com/antfu/i18n-ally/blob/master/README.md)中了解有关 i18n Ally 的更多信息。 ### i18nPlugin (intellij 平台) [i18nPlugin](https://github.com/nyavro/i18nPlugin) Intellij idea i18next 支持插件([Jetbrains 插件页面](https://plugins.jetbrains.com/plugin/12981-i18n-support))。 适用于 i18n typescript/javascript/PHP 的插件。 支持 vue-i18n。 要启用 vue-i18n 支持,请转到 设置- > 工具 -> i18n 插件配置,然后选中 "Vue-i18n"。 您需要设置您的语言环境目录(默认为语言环境)。 ### vue-i18n-extract [vue-i18n-extract](https://github.com/pixari/vue-i18n-extract) 对基于 vue-i18n 的 Vue.js 项目执行静态分析,并报告以下信息: - 所有 **未使用的 vue-i18n 键**的列表(在语言文件中找到但在项目中未使用的条目) - 所有 **缺失键** 的列表(在项目中使用但在语言文件中不存在的条目) 可以在控制台中显示输出或将其写入json文件 丢失的键也可以自动添加到给定的语言文件中 ================================================ FILE: vuepress/zh/installation.md ================================================ # 安装 ## 兼容性说明 - Vue.js `2.0.0`+ ## 直接下载 / CDN [unpkg.com](https://unpkg.com) 提供了基于 NPM 的 CDN 链接。上面的链接会一直指向在 NPM 发布的最新版本。你也可以通过 这样的 URL 指定版本号或者 tag。 在 Vue 之后引入 vue-i18n,它会自动安装: ```html ``` ## NPM ```sh npm install vue-i18n ``` ## Yarn ```sh yarn add vue-i18n ``` 如果在一个模块系统中使用它,你必须通过 `Vue.use()` 明确地安装 `vue-i18n`: ```javascript import Vue from 'vue' import VueI18n from 'vue-i18n' Vue.use(VueI18n) ``` 如果使用全局的 script 标签,则无须如此 (手动安装) `

{{ $t("message.hello") }}

``` ## JavaScript ```js // 如果使用模块系统 (例如通过 vue-cli),则需要导入 Vue 和 VueI18n ,然后调用 Vue.use(VueI18n)。 // import Vue from 'vue' // import VueI18n from 'vue-i18n' // // Vue.use(VueI18n) // 准备翻译的语言环境信息 const messages = { en: { message: { hello: 'hello world' } }, ja: { message: { hello: 'こんにちは、世界' } } } // 通过选项创建 VueI18n 实例 const i18n = new VueI18n({ locale: 'ja', // 设置地区 messages, // 设置地区信息 }) // 通过 `i18n` 选项创建 Vue 实例 new Vue({ i18n }).$mount('#app') // 现在应用程序已经准备好了! ``` 输出如下: ```html

こんにちは、世界

```