diff --git a/packages/@vue/cli-ui/src/components/PromptList.vue b/packages/@vue/cli-ui/src/components/PromptList.vue
index edc877b56..c86a5cc97 100644
--- a/packages/@vue/cli-ui/src/components/PromptList.vue
+++ b/packages/@vue/cli-ui/src/components/PromptList.vue
@@ -5,8 +5,8 @@
>
@@ -37,7 +37,7 @@ export default {
methods: {
generateLabel (choice) {
- let label = choice.name
+ let label = this.$t(choice.name)
if (choice.isDefault) {
label += ` (${this.$t('components.prompt-list.default')})`
}
diff --git a/packages/@vue/cli-ui/src/graphql-api/channels.js b/packages/@vue/cli-ui/src/graphql-api/channels.js
index 7459900b6..c8ed274f6 100644
--- a/packages/@vue/cli-ui/src/graphql-api/channels.js
+++ b/packages/@vue/cli-ui/src/graphql-api/channels.js
@@ -11,5 +11,6 @@ module.exports = {
CLIENT_ADDON_ADDED: 'client_addon_added',
SHARED_DATA_UPDATED: 'shared_data_updated',
PLUGIN_ACTION_CALLED: 'plugin_action_called',
- PLUGIN_ACTION_RESOLVED: 'plugin_action_resolved'
+ PLUGIN_ACTION_RESOLVED: 'plugin_action_resolved',
+ LOCALE_ADDED: 'locale_added'
}
diff --git a/packages/@vue/cli-ui/src/graphql-api/connectors/client-addons.js b/packages/@vue/cli-ui/src/graphql-api/connectors/client-addons.js
index 1bd705fb6..f6b16ad2b 100644
--- a/packages/@vue/cli-ui/src/graphql-api/connectors/client-addons.js
+++ b/packages/@vue/cli-ui/src/graphql-api/connectors/client-addons.js
@@ -2,7 +2,7 @@ const path = require('path')
// Subs
const channels = require('../channels')
// Utils
-const { getBasePath } = require('../utils/serve')
+const { resolveModuleRoot } = require('../utils/resolve-path')
let addons = []
@@ -43,7 +43,7 @@ function serve (req, res) {
const { id, 0: file } = req.params
const addon = findOne(id)
if (addon && addon.path) {
- const basePath = getBasePath(require.resolve(addon.path))
+ const basePath = resolveModuleRoot(require.resolve(addon.path))
if (basePath) {
res.sendFile(path.join(basePath, file))
return
diff --git a/packages/@vue/cli-ui/src/graphql-api/connectors/locales.js b/packages/@vue/cli-ui/src/graphql-api/connectors/locales.js
new file mode 100644
index 000000000..4630e9f0d
--- /dev/null
+++ b/packages/@vue/cli-ui/src/graphql-api/connectors/locales.js
@@ -0,0 +1,26 @@
+// Subs
+const channels = require('../channels')
+
+let locales = []
+
+function list (context) {
+ return locales
+}
+
+function add ({ lang, strings }, context) {
+ const locale = { lang, strings }
+ locales.push(locale)
+ context.pubsub.publish(channels.LOCALE_ADDED, {
+ localeAdded: locale
+ })
+}
+
+function clear (context) {
+ locales = []
+}
+
+module.exports = {
+ list,
+ add,
+ clear
+}
diff --git a/packages/@vue/cli-ui/src/graphql-api/connectors/plugins.js b/packages/@vue/cli-ui/src/graphql-api/connectors/plugins.js
index dceec6875..abf418bd8 100644
--- a/packages/@vue/cli-ui/src/graphql-api/connectors/plugins.js
+++ b/packages/@vue/cli-ui/src/graphql-api/connectors/plugins.js
@@ -17,6 +17,7 @@ const {
} = require('@vue/cli/lib/util/installDeps')
const invoke = require('@vue/cli/lib/invoke')
const notifier = require('node-notifier')
+const globby = require('globby')
// Subs
const channels = require('../channels')
// Connectors
@@ -27,11 +28,12 @@ const progress = require('./progress')
const logs = require('./logs')
const clientAddons = require('./client-addons')
const views = require('./views')
+const locales = require('./locales')
// Api
const PluginApi = require('../api/PluginApi')
// Utils
const { getCommand } = require('../utils/command')
-const { getBasePath } = require('../utils/serve')
+const { resolveModuleRoot } = require('../utils/resolve-path')
const ipc = require('../utils/ipc')
const PROGRESS_ID = 'plugin-installation'
@@ -54,7 +56,7 @@ let installationStep
let projectId
function getPath (id) {
- return path.dirname(resolveModule(id, cwd.get()))
+ return resolveModuleRoot(resolveModule(id, cwd.get()), id)
}
function findPlugins (deps) {
@@ -91,7 +93,7 @@ function resetPluginApi (context) {
// Run Plugin API
runPluginApi('@vue/cli-service', context)
plugins.forEach(plugin => runPluginApi(plugin.id, context))
- runPluginApi('.', context, 'vue-cli-ui')
+ runPluginApi(cwd.get(), context, 'vue-cli-ui')
// Add client addons
pluginApi.clientAddons.forEach(options => clientAddons.add(options, context))
// Add views
@@ -121,6 +123,18 @@ function runPluginApi (id, context, fileName = 'ui') {
module(pluginApi)
pluginApi.pluginId = null
}
+
+ // Locales
+ try {
+ const folder = fs.existsSync(id) ? id : getPath(id)
+ const paths = globby.sync([path.join(folder, './locales/*.json')])
+ paths.forEach(file => {
+ const basename = path.basename(file)
+ const lang = basename.substr(0, basename.indexOf('.'))
+ const strings = JSON.parse(fs.readFileSync(file, { encoding: 'utf8' }))
+ locales.add({ lang, strings }, context)
+ })
+ } catch (e) {}
}
function findOne (id, context) {
@@ -367,7 +381,7 @@ async function callAction ({ id, params }, context) {
function serve (req, res) {
const { id, 0: file } = req.params
- const basePath = getBasePath(require.resolve(id), id)
+ const basePath = id === '.' ? cwd.get() : getPath(id)
if (basePath) {
res.sendFile(path.join(basePath, 'ui-public', file))
return
diff --git a/packages/@vue/cli-ui/src/graphql-api/connectors/projects.js b/packages/@vue/cli-ui/src/graphql-api/connectors/projects.js
index 662a28b03..f8e79daa4 100644
--- a/packages/@vue/cli-ui/src/graphql-api/connectors/projects.js
+++ b/packages/@vue/cli-ui/src/graphql-api/connectors/projects.js
@@ -14,6 +14,7 @@ const cwd = require('./cwd')
const prompts = require('./prompts')
const folders = require('./folders')
const plugins = require('./plugins')
+const locales = require('./locales')
// Context
const getContext = require('../context')
@@ -316,6 +317,8 @@ async function open (id, context) {
lastProject = currentProject
currentProject = project
cwd.set(project.path, context)
+ // Reset locales
+ locales.clear()
// Load plugins
plugins.list(project.path, context)
diff --git a/packages/@vue/cli-ui/src/graphql-api/resolvers.js b/packages/@vue/cli-ui/src/graphql-api/resolvers.js
index 73d3e7c07..327ef8d62 100644
--- a/packages/@vue/cli-ui/src/graphql-api/resolvers.js
+++ b/packages/@vue/cli-ui/src/graphql-api/resolvers.js
@@ -12,6 +12,7 @@ const progress = require('./connectors/progress')
const files = require('./connectors/files')
const clientAddons = require('./connectors/client-addons')
const sharedData = require('./connectors/shared-data')
+const locales = require('./connectors/locales')
// Start ipc server
require('./utils/ipc')
@@ -37,7 +38,8 @@ const resolvers = [{
cwd: () => cwd.get(),
progress: (root, { id }, context) => progress.get(id, context),
clientAddons: (root, args, context) => clientAddons.list(context),
- sharedData: (root, { id }, context) => sharedData.get(id, context)
+ sharedData: (root, { id }, context) => sharedData.get(id, context),
+ locales: (root, args, context) => locales.list(context)
},
Mutation: {
@@ -73,6 +75,9 @@ const resolvers = [{
(parent, args, { pubsub }) => pubsub.asyncIterator(channels.SHARED_DATA_UPDATED),
(payload, vars) => payload.sharedDataUpdated.id === vars.id
)
+ },
+ localeAdded: {
+ subscribe: (parent, args, { pubsub }) => pubsub.asyncIterator(channels.LOCALE_ADDED)
}
}
}]
diff --git a/packages/@vue/cli-ui/src/graphql-api/type-defs.js b/packages/@vue/cli-ui/src/graphql-api/type-defs.js
index 091260711..f0d004b20 100644
--- a/packages/@vue/cli-ui/src/graphql-api/type-defs.js
+++ b/packages/@vue/cli-ui/src/graphql-api/type-defs.js
@@ -54,11 +54,17 @@ type SharedData {
value: JSON
}
+type Locale {
+ lang: String!
+ strings: JSON!
+}
+
type Query {
progress (id: ID!): Progress
cwd: String!
clientAddons: [ClientAddon]
sharedData (id: ID!): SharedData
+ locales: [Locale]
}
type Mutation {
@@ -72,6 +78,7 @@ type Subscription {
cwdChanged: String!
clientAddonAdded: ClientAddon
sharedDataUpdated (id: ID!): SharedData
+ localeAdded: Locale
}
`]
diff --git a/packages/@vue/cli-ui/src/graphql-api/utils/serve.js b/packages/@vue/cli-ui/src/graphql-api/utils/resolve-path.js
similarity index 82%
rename from packages/@vue/cli-ui/src/graphql-api/utils/serve.js
rename to packages/@vue/cli-ui/src/graphql-api/utils/resolve-path.js
index 91554fa80..d579d22ae 100644
--- a/packages/@vue/cli-ui/src/graphql-api/utils/serve.js
+++ b/packages/@vue/cli-ui/src/graphql-api/utils/resolve-path.js
@@ -1,4 +1,4 @@
-exports.getBasePath = function (filePath, id = null) {
+exports.resolveModuleRoot = function (filePath, id = null) {
{
const index = filePath.lastIndexOf('/index.js')
if (index !== -1) {
diff --git a/packages/@vue/cli-ui/src/graphql/localeAdded.gql b/packages/@vue/cli-ui/src/graphql/localeAdded.gql
new file mode 100644
index 000000000..988b0907d
--- /dev/null
+++ b/packages/@vue/cli-ui/src/graphql/localeAdded.gql
@@ -0,0 +1,7 @@
+#import "./localeFragment.gql"
+
+subscription localeAdded {
+ localeAdded {
+ ...locale
+ }
+}
diff --git a/packages/@vue/cli-ui/src/graphql/localeFragment.gql b/packages/@vue/cli-ui/src/graphql/localeFragment.gql
new file mode 100644
index 000000000..ecf506ad4
--- /dev/null
+++ b/packages/@vue/cli-ui/src/graphql/localeFragment.gql
@@ -0,0 +1,4 @@
+fragment locale on Locale {
+ lang
+ strings
+}
diff --git a/packages/@vue/cli-ui/src/graphql/locales.gql b/packages/@vue/cli-ui/src/graphql/locales.gql
new file mode 100644
index 000000000..a5fed39b5
--- /dev/null
+++ b/packages/@vue/cli-ui/src/graphql/locales.gql
@@ -0,0 +1,7 @@
+#import "./localeFragment.gql"
+
+query locales {
+ locales {
+ ...locale
+ }
+}
diff --git a/packages/@vue/cli-ui/src/i18n.js b/packages/@vue/cli-ui/src/i18n.js
index db9e52d3a..8a09e223d 100644
--- a/packages/@vue/cli-ui/src/i18n.js
+++ b/packages/@vue/cli-ui/src/i18n.js
@@ -1,5 +1,6 @@
import Vue from 'vue'
import VueI18n from 'vue-i18n'
+import deepmerge from 'deepmerge'
Vue.use(VueI18n)
@@ -30,4 +31,9 @@ const i18n = new VueI18n({
messages: loadLocaleMessages()
})
+export function mergeLocale (lang, messages) {
+ const newData = deepmerge(i18n.getLocaleMessage(lang), messages)
+ i18n.setLocaleMessage(lang, newData)
+}
+
export default i18n
diff --git a/packages/@vue/cli-ui/src/locales/fr.json b/packages/@vue/cli-ui/src/locales/fr.json
index 21a012635..30cfb09eb 100644
--- a/packages/@vue/cli-ui/src/locales/fr.json
+++ b/packages/@vue/cli-ui/src/locales/fr.json
@@ -62,7 +62,8 @@
"tooltips": {
"plugins": "Plugins",
"configuration": "Configuration",
- "tasks": "Tâches"
+ "tasks": "Tâches",
+ "more": "Plus"
}
},
"project-select-list": {
@@ -275,6 +276,12 @@
"project-configurations": {
"title": "Configuration du projet"
},
+ "project-configuration-details": {
+ "actions": {
+ "cancel": "Annuler les changements",
+ "save": "Sauvegarder les modifications"
+ }
+ },
"project-tasks": {
"title": "Tâches du projet"
},
@@ -288,6 +295,13 @@
"parameters": "Paramètres",
"more-info": "Plus d'infos",
"output": "Sortie"
+ },
+ "about": {
+ "title": "A propos",
+ "description": "
@vue/cli-ui est un paquet inclus dans @vue/cli qui affiche une interface graphique.",
+ "quote": "Vue-cli 3.x is a complete rewrite, with a lot of new awesome features. You will be to select features like routing, Vuex or Typescript, then add and upgrade building blocks called \"vue-cli plugins\". But having so much more options also means the tool is now more complex and harder to start using. That's why we thought having a full-blown GUI would help discover the new features, search and install vue-cli plugins and unlock more possibilities overall while not being limited by a terminal interface. To sum up, vue-cli will not only allow you to bootstrap a new project easily, but it will also remain useful for ongoing work afterwards!",
+ "links": "Liens utiles",
+ "back": "Retour"
}
}
}
diff --git a/packages/@vue/cli-ui/src/util/ClientAddonApi.js b/packages/@vue/cli-ui/src/util/ClientAddonApi.js
index 1fa656fa1..27a9b37fd 100644
--- a/packages/@vue/cli-ui/src/util/ClientAddonApi.js
+++ b/packages/@vue/cli-ui/src/util/ClientAddonApi.js
@@ -1,5 +1,6 @@
import Vue from 'vue'
import router from '../router'
+import { mergeLocale } from '../i18n'
import ProjectHome from '../views/ProjectHome.vue'
export default class ClientAddonApi {
@@ -18,7 +19,7 @@ export default class ClientAddonApi {
this.components.set(id, definition)
const componentId = toComponentId(id)
Vue.component(componentId, definition)
- console.log(`Registered ${componentId} component`)
+ console.log(`[ClientAddonApi] Registered ${componentId} component`)
// Call listeners
const listeners = this.componentListeners.get(id)
if (listeners) {
@@ -47,6 +48,18 @@ export default class ClientAddonApi {
children: routes
}
])
+ console.log(`[ClientAddonApi] Registered new routes under the /addon/${id} route`)
+ }
+
+ /**
+ * Merge new strings into the specified lang translations (using vue-i18n).
+ *
+ * @param {string} lang Locale to merge to (ex: 'en', 'fr'...)
+ * @param {object} strings A vue-i18n strings object containing the translations
+ */
+ addLocalization (lang, strings) {
+ mergeLocale(lang, strings)
+ console.log(`[ClientAddonApi] Registered new strings for locale ${lang}`)
}
/* Internal */
diff --git a/packages/@vue/cli-ui/src/views/ProjectTaskDetails.vue b/packages/@vue/cli-ui/src/views/ProjectTaskDetails.vue
index 5b8e76771..0b2764c05 100644
--- a/packages/@vue/cli-ui/src/views/ProjectTaskDetails.vue
+++ b/packages/@vue/cli-ui/src/views/ProjectTaskDetails.vue
@@ -4,7 +4,7 @@
@@ -66,7 +66,7 @@
:key="view.id"
:value="view.id"
:icon-left="view.icon"
- :label="view.label"
+ :label="$t(view.label)"
/>