Commit c52002e4 authored by dragondean@qq.com's avatar dragondean@qq.com

国际化

parent 40eb52de
Stack trace:
Frame Function Args
00800000010 0018006401E (001802839A0, 0018026EFD1, 00000000059, 000FFFFB700)
00800000010 0018004973A (00800000010, 00100000000, 00000000000, 00000000001)
00800000010 00180049772 (00000000000, 00000000000, 00000000059, 00180354100)
00800000010 00180070FE9 (00000000000, 000FFFFC7D0, 0000000000B, 00000000000)
00800000010 001800712A0 (00000000003, 000FFFFC8F0, 00180045BCF, 000FFFFC8F0)
00800000010 00180072AB9 (000FFFFC8F0, 00180271795, 001801004A7, 0000000000D)
00800000010 0018005B2A3 (7FF8018B1490, 00000000000, 00000000000, 000FFFFFFFF)
00800000010 0018005C655 (00800029390, 1D400000000, 00000000000, 00000000008)
00800000010 0018005CB3F (000FFFFCCE0, 000FFFFCC45, 00000000000, 205C745C615C635C)
000FFFFCBBC 0018005CF2F (635C655C205C265C, 3E5C205C6F5C685C, 765C655C645C2F5C, 795C745C745C2F5C)
000FFFFCCE0 00180049EE8 (00000000000, 00000000000, 00000000000, 00000000000)
000FFFFFFF0 00180048846 (00000000000, 00000000000, 00000000000, 00000000000)
000FFFFFFF0 001800488F4 (00000000000, 00000000000, 00000000000, 00000000000)
End of stack trace
Stack trace:
Frame Function Args
00800000010 0018006401E (001802839A0, 0018026EFD1, 00000000059, 000FFFFB710)
00800000010 0018004973A (00800000010, 00100000000, 00000000000, 00000000001)
00800000010 00180049772 (00000000000, 00000000000, 00000000059, 00180353FD0)
00800000010 00180070FE9 (00000000002, 000FFFFC7E0, 0000000000B, 00000000000)
00800000010 001800712A0 (000FFFFCBC0, 00800000010, 00180045B55, 000FFFFC900)
00800000010 00180072AB9 (000FFFFC900, 00180271795, 001801004A7, 0000000000D)
00800000010 0018005B2A3 (7FF8018B1490, 00000000000, 00000000000, 040FFFFFFFF)
00800000010 0018005C655 (00000000002, 00180350D08, 00000000002, 00000000008)
00800000010 0018005CB3F (000FFFFCC9C, 000FFFFCCE0, 000FFFFCC9D, 000FFFFCC00)
000FFFFCBCC 0018005CF2F (6D5C2D5C6C5C615C, 675C615C6E5C615C, 635C2D5C725C655C, 205C655C725C6F5C)
000FFFFCCE0 00180049EE8 (00000000000, 00000000000, 00000000000, 00000000000)
000FFFFFFF0 00180048846 (00000000000, 00000000000, 00000000000, 00000000000)
000FFFFFFF0 001800488F4 (00000000000, 00000000000, 00000000000, 00000000000)
End of stack trace
import Vue from 'vue'
import VueI18n from './vue-i18n/dist/vue-i18n.common'
import {getLocale} from '@/utils/db'
Vue.use(VueI18n)
const i18n = new VueI18n({
locale: getLocale() || 'zh_CN',
formatFallbackMessages: true,
messages: {
'en_US': require('./languages/en_US.json'),
'zh_CN': require('./languages/zh_CN.json')
}
})
console.log({i18n})
/* Vue.filter('$t', Vue.$i18n)
*/
// 重新console.warn来捕获未翻译的内容
/* console.wareOrig = console.warn
console.warn = (...data) => {
console.log('warn', typeof data)
console.wareOrig(data)
} */
export default i18n
\ No newline at end of file
{
"登录": "Login",
"个人中心": "User Center",
"布局设置": "Layout"
}
\ No newline at end of file
{
"登录": "登录"
}
\ No newline at end of file
This diff is collapsed.
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.
<p align="center"><img width="128px" height="112px" src="./assets/vue-i18n-logo.png" alt="Vue I18n logo"></p>
<h1 align="center">vue-i18n</h1>
<p align="center">
<a href="https://circleci.com/gh/kazupon/vue-i18n/tree/dev"><img src="https://circleci.com/gh/kazupon/vue-i18n/tree/dev.svg?style=shield" alt="Build Status"></a>
<a href="http://badge.fury.io/js/vue-i18n"><img src="https://badge.fury.io/js/vue-i18n.svg" alt="NPM version"></a>
<a href="https://discord.gg/4yCnk2m"><img src="https://img.shields.io/badge/Discord-join%20chat-738bd7.svg" alt="vue-i18n channel on Discord"></a>
</p>
<p align="center">Internationalization plugin for Vue.js</p>
<br/>
<h3 align="center">🏅 Platinum Sponsors</h3>
<p align="center">
<a href="https://zenarchitects.co.jp/" target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/zenarchitects.png"
width="400px"
/>
</a>
</p>
<h3 align="center">✨ Special Sponsors</h3>
<p align="center">
<a
href="https://plaid.co.jp/"
target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/plaid.svg"
width="240px"
/>
</a>
</p>
<h3 align="center">🥇 Gold Sponsors</h3>
<p align="center">
<a
href="https://nuxtjs.org/"
target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/nuxt.png"
width="240px"
/>
</a>
</p>
<p align="center">
<a
href="https://rapidapi.com/"
target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/RapidAPI.svg"
width="240px"
/>
</a>
</p>
<p align="center">
<a
href="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"
target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/localazy.svg"
width="240px"
/>
</a>
</p>
<p align="center">
<a
href="https://crowdin.com/teams/engineering?utm_source=vue-i18n.intlify.dev&utm_medium=referral"
target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/crowdin.svg"
width="240px"
/>
</a>
</p>
<h3 align="center">🥈 Silver Sponsors</h3>
<h3 align="center">🥉 Bronze Sponsors</h3>
<p align="center">
<a href="https://www.sendcloud.com/" target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/sendcloud.png"
width="144px"
/>
</a>
</p>
<p align="center">
<a href="https://www.vuemastery.com/" target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/vuemastery.png"
width="144px"
/>
</a>
</p>
<p align="center">
<a href="https://www.deci-bel.com/" target="_blank">
<img
src="https://raw.githubusercontent.com/kazupon/vue-i18n/v8.x/vuepress/.vuepress/public/patrons/decibel.png"
width="144px"
/>
</a>
</p>
<br/>
## ⚠️ 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)
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<LocaleMessage>;
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<FormattedNumberPart>;
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<any> | null)
};
declare type $npm$Vue$Dictionaly<T> = { [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<Function>,
silent: boolean,
productionTip: boolean,
performance: boolean,
devtools: boolean,
errorHandler: ?(err: Error, vm: Vue, info: string) => void,
ignoredElements: Array<string>,
keyCodes: $npm$Vue$Dictionaly<number>,
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,
}
{
"_from": "vue-i18n@^8.27.2",
"_id": "vue-i18n@8.27.2",
"_inBundle": false,
"_integrity": "sha512-QVzn7u2WVH8F7eSKIM00lujC7x1mnuGPaTnDTmB01Hd709jDtB9kYtBqM+MWmp5AJRx3gnqAdZbee9MelqwFBg==",
"_location": "/vue-i18n",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "vue-i18n@^8.27.2",
"name": "vue-i18n",
"escapedName": "vue-i18n",
"rawSpec": "^8.27.2",
"saveSpec": null,
"fetchSpec": "^8.27.2"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.27.2.tgz",
"_shasum": "b649a65ff42b7d1a482679b732902f889965a068",
"_spec": "vue-i18n@^8.27.2",
"_where": "D:\\projects\\jiedao-admin",
"author": {
"name": "kazuya kawaguchi",
"email": "kawakazu80@gmail.com"
},
"bugs": {
"url": "https://github.com/kazupon/vue-i18n/issues"
},
"bundleDependencies": false,
"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"
}
},
"deprecated": false,
"description": "Internationalization plugin for Vue.js",
"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": "^102.0.0",
"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"
],
"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",
"name": "vue-i18n",
"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 BABEL_ENV=test webpack-dev-server --inline --hot --open --content-base ./test/unit/ --config config/webpack.dev.conf.js",
"docs:build": "cross-env NODE_ENV=production node config/version.js && cross-env NODE_ENV=production vuepress build vuepress -d docs",
"docs:clean": "rm -rf docs/**",
"docs:dev": "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 BABEL_ENV=test karma start config/karma.cover.conf.js",
"test:e2e": "npm run build && node test/e2e/runner.js",
"test:types": "tsc -p types",
"test:unit": "cross-env BABEL_ENV=test karma start config/karma.unit.conf.js",
"test:unit:ci": "cross-env BABEL_ENV=test karma start config/karma.unit.ci.conf.js"
},
"sideEffects": false,
"types": "types/index.d.ts",
"unpkg": "dist/vue-i18n.js",
"version": "8.27.2",
"vetur": {
"tags": "vetur/tags.json",
"attributes": "vetur/attributes.json"
}
}
/* @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)
}
/* @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
}
}
/* @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<any> {
const params: Array<any> = []
locale && params.push(locale)
if (args && (Array.isArray(args) || isPlainObject(args))) {
params.push(args)
}
return params
}
/* @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)
}
}
/* @flow */
import { warn, isObject } from './util'
export default class BaseFormatter {
_caches: { [key: string]: Array<Token> }
constructor () {
this._caches = Object.create(null)
}
interpolate (message: string, values: any): Array<any> {
if (!values) {
return [message]
}
let tokens: Array<Token> = 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<Token> {
const tokens: Array<Token> = []
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<Token>, values: Object | Array<any>): Array<any> {
const compiled: Array<any> = []
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
}
This diff is collapsed.
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
}
}
/* @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
}
})
}
}
}
/* @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<string> {
const keys: Array<string> = []
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<Function> = []
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<PathValue>
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<string> {
let hit: ?Array<string> = 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<string> = 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
}
}
}
/* @flow */
/**
* constants
*/
export const numberFormatKeys = [
'compactDisplay',
'currency',
'currencyDisplay',
'currencySign',
'localeMatcher',
'notation',
'numberingSystem',
'signDisplay',
'style',
'unit',
'unitDisplay',
'useGrouping',
'minimumIntegerDigits',
'minimumFractionDigits',
'maximumFractionDigits',
'minimumSignificantDigits',
'maximumSignificantDigits'
]
/**
* 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<mixed>): 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<any>, item: any): Set<any> | void {
if (arr.delete(item)) {
return arr
}
}
export function arrayFrom (arr: Set<any>): Array<any> {
const ret = []
arr.forEach(a => ret.push(a))
return ret
}
export function includes (arr: Array<any>, 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<string> = Object.keys(a)
const keysB: Array<string> = 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, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;')
}
/**
* 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
}
This diff is collapsed.
{
"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"
}
}
{
"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"
}
}
......@@ -27,15 +27,17 @@
<screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip content="布局大小" effect="dark" placement="bottom">
<el-tooltip :content="$t('布局大小')" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
</template>
<el-select v-model="locale" placeholder="语言" class="right-menu-item select-nav" @change="localeChange">
<!-- <el-select v-model="locale" placeholder="语言" class="right-menu-item select-nav" @change="localeChange">
<el-option v-for="dict in langDatas" :key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-select> -->
<dict-selector :type="DICT_TYPE.SYSTEM_LOCALE" v-model="lang" defaultable class="right-menu-item select-nav" />
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
<div class="avatar-wrapper">
......@@ -44,7 +46,7 @@
</div>
<el-dropdown-menu slot="dropdown">
<router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item>{{$t('个人中心')}}</el-dropdown-item>
</router-link>
<el-dropdown-item @click.native="setting = true">
<span>布局设置</span>
......@@ -69,23 +71,23 @@ import Search from '@/components/HeaderSearch'
import RuoYiGit from '@/components/RuoYi/Git'
import RuoYiDoc from '@/components/RuoYi/Doc'
import {getLocale, saveLocale} from "@/utils/db";
import {LangEnum} from "@/utils/constants";
import unreadMessage from "@/assets/images/unread-message.png"
import i18n from '@/i18n'
export default {
data() {
return {
unreadMessage,
locale: getLocale(),
// 枚举
langDatas: LangEnum.LANG,
// langDatas: LangEnum.LANG,
notReadTotal:0,//要去取VUEX里面的未读数据总数,我不会,登录之后要调得到当前人未读记录总数接口放到VUEX中
lang: i18n.locale, // 当前语言
}
},
created() {
this.$store.dispatch('getNotMessage');
this.$store.dispatch('getToDoList');
},
},
components: {
Breadcrumb,
TopNav,
......@@ -126,14 +128,22 @@ export default {
}
},
watch:{
lang(val){
console.log('切换了语言', val)
this.$i18n.locale = val
saveLocale(val)
}
},
methods: {
toggleSideBar() {
this.$store.dispatch('app/toggleSideBar')
},
localeChange(value) {
/* localeChange(value) {
console.log(value)
saveLocale(value)
},
}, */
async logout() {
this.$modal.confirm('确定注销并退出系统吗?', '提示').then(() => {
this.$store.dispatch('LogOut').then(() => {
......
import Vue from 'vue'
import Cookies from 'js-cookie'
import Element from 'element-ui'
import './assets/styles/element-variables.scss'
import '@/assets/styles/index.scss' // global css
import '@/assets/styles/ruoyi.scss' // ruoyi css
import App from './App'
......@@ -22,13 +19,13 @@ import { parseTime, resetForm, addDateRange, addBeginAndEndTime, handleTree} fro
import Pagination from "@/components/Pagination";
// 自定义表格工具扩展
import RightToolbar from "@/components/RightToolbar"
import TableForm from '@/components/TableForm'
import DictSelector from '@/components/DictSelector'
// 代码高亮插件
// import hljs from 'highlight.js'
// import 'highlight.js/styles/github-gist.css'
import {DICT_TYPE, getDictDataLabel, getDictDatas, getDictDatas2} from "@/utils/dict";
import i18n from './i18n'
console.log({i18n})
// 全局方法挂载
Vue.prototype.getDicts = getDicts
Vue.prototype.getConfigKey = getConfigKey
......@@ -69,7 +66,7 @@ Vue.component('DictTag', DictTag)
Vue.component('DocAlert', DocAlert)
Vue.component('Pagination', Pagination)
Vue.component('RightToolbar', RightToolbar)
Vue.component('TableForm', TableForm)
// Vue.component('TableForm', TableForm)
Vue.component('DictSelector', DictSelector)
// 字典标签组件
import DictTag from '@/components/DictTag'
......@@ -119,5 +116,6 @@ new Vue({
el: '#app',
router,
store,
i18n,
render: h => h(App)
})
......@@ -29,6 +29,7 @@ export const DICT_TYPE = {
SYSTEM_ERROR_CODE_TYPE: 'system_error_code_type',
INTERNAL_MESSAGE_TYPE: 'internal_message_type',
INTERNAL_MESSAGE_READ_STATUS: 'internal_message_read_status',
SYSTEM_LOCALE: 'system_locale', // 语言列表
HELP_DOC_STATUS: 'help_doc_status',
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment