diff --git a/docs/.vuepress/components/CountrySelect.vue b/docs/.vuepress/components/CountrySelect.vue index 99f5036..6b61752 100644 --- a/docs/.vuepress/components/CountrySelect.vue +++ b/docs/.vuepress/components/CountrySelect.vue @@ -12,5 +12,3 @@ export default { }), } - - diff --git a/docs/.vuepress/components/TranslationFrench.vue b/docs/.vuepress/components/TranslationFrench.vue new file mode 100644 index 0000000..fdd279b --- /dev/null +++ b/docs/.vuepress/components/TranslationFrench.vue @@ -0,0 +1,18 @@ + + + diff --git a/docs/.vuepress/components/TranslationSingleKey.vue b/docs/.vuepress/components/TranslationSingleKey.vue new file mode 100644 index 0000000..5dda94a --- /dev/null +++ b/docs/.vuepress/components/TranslationSingleKey.vue @@ -0,0 +1,16 @@ + + + diff --git a/docs/.vuepress/components/TranslationSpanish.vue b/docs/.vuepress/components/TranslationSpanish.vue new file mode 100644 index 0000000..fe21094 --- /dev/null +++ b/docs/.vuepress/components/TranslationSpanish.vue @@ -0,0 +1,17 @@ + + + diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index ee16a29..0d0b8d7 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -2,6 +2,7 @@ const { description } = require('./config/meta') const head = require('./config/head') const plugins = require('./config/plugins') const themeConfig = require('./config/themeConfig') +const { resolve } = require('path') module.exports = { title: 'Vue Select', @@ -9,4 +10,7 @@ module.exports = { head, plugins, themeConfig, + alias: { + 'vue-select': resolve(__dirname, '../../src'), + }, } diff --git a/docs/.vuepress/config/themeConfig.js b/docs/.vuepress/config/themeConfig.js index 456abcf..edfb36a 100644 --- a/docs/.vuepress/config/themeConfig.js +++ b/docs/.vuepress/config/themeConfig.js @@ -37,7 +37,7 @@ module.exports = { collapsable: false, children: [ ['guide/accessibility', 'WAI-ARIA Spec'], - ['guide/localization', 'Localization'], + ['guide/localization', 'Localization / i18n'], ], }, { diff --git a/docs/.vuepress/enhanceApp.js b/docs/.vuepress/enhanceApp.js index 62f5b3b..333ab67 100644 --- a/docs/.vuepress/enhanceApp.js +++ b/docs/.vuepress/enhanceApp.js @@ -1,4 +1,4 @@ -import vSelect from '../../src/components/Select' +import vSelect from 'vue-select/components/Select' export default ({ Vue, options, router, siteData }) => { Vue.component('v-select', vSelect) diff --git a/docs/guide/localization.md b/docs/guide/localization.md index 412edb5..c7f3a88 100644 --- a/docs/guide/localization.md +++ b/docs/guide/localization.md @@ -1,45 +1,78 @@ +# Translations + +There are a number of strings used within the component that can all be translated. Out of the box, +Vue Select has translations for the following languages/locales that can be found +in [`src/locales`](https://github.com/sagalbot/vue-select/tree/master/src/locales): + +- [English (default)](https://github.com/sagalbot/vue-select/tree/master/src/locales/en.js) +- [French](https://github.com/sagalbot/vue-select/tree/master/src/locales/fr.js) +- [Spanish](https://github.com/sagalbot/vue-select/tree/master/src/locales/es.js) + +Translations can be set on for each component instance, or globally for all instances in your app. +Both methods will use the `locale` prop to determine the text to be used throughout the component. + +**Locale Prop** + +The `locale` prop accepts a function and should return an object of translated strings. The function +receives the default english translations for the component. This allows you to override the whole +object, or change just the values you need. The structure of the returned `locale` object is +described below. + +**Locale Object** + +The object returned from the locale prop requires specific keys to be set. Below is an example from +the english locale that ships with Vue Select: + +<<< @/../src/locales/en.js + +# Examples +--- + +## Translate a Single Instance + +Translating a single instance of Vue Select can be accomplished by setting the `locale` function to +return an object with the translations you need. In the example below, we use the french `locale` +object that ships with Vue Select. + + +<<< @/.vuepress/components/TranslationFrench.vue + +**Modifying Specific Text** + +You can also use the `locale` prop to override just the text that you'd like to change. In this +example, the text in the `no-options` slot is updated to say 'No Options found!!'. + + +<<< @/.vuepress/components/TranslationSingleKey.vue + +## Translate all Instances + +If you'd like to translate the text for **all** Vue Select instances in your app, you can override +the default prop when registering the component. In the example below, all instances of Vue Select +will be translated to Spanish. + + +<<< @/.vuepress/components/TranslationSpanish.vue + ## Right to Left Vue Select supports RTL using the standard HTML API using the `dir` prop. -```html +```vue + ``` -The `dir` prop accepts the same values as the [HTML spec](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir): +The `dir` prop accepts the same values as +the [HTML spec](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir): - - `rtl` - - `ltr` - - `auto` +- `rtl` +- `ltr` +- `auto` -## Component Text +## Contributing Languages -All of the text within the component has been wrapped within [slots](https://vuejs.org/v2/guide/components.html#Content-Distribution-with-Slots) and can be replaced in your app. - -### Loading Spinner -*Slot Definition:* -```html - -
Loading...
-
-``` -*Implementation:* -```html - - - -``` - -### No Options Text -*Slot Definition:* -```html -Sorry, no matching options. -``` -*Implementation:* -```html - -
No Options Here!
-
-``` - -For a full list of component slots, view the [slots API docs](../api/slots.md). +If you'd like to see Vue Select translated to a new language, submit a PR with a new +`locale` file in the `src/locales` folder. The file name should match +the [ISO 639-1 code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) for the language, and +export a `locale` object. diff --git a/src/components/Select.vue b/src/components/Select.vue index 7bfc85b..67bea76 100644 --- a/src/components/Select.vue +++ b/src/components/Select.vue @@ -64,8 +64,8 @@ :disabled="disabled" type="button" class="vs__clear" - title="Clear Selected" - aria-label="Clear Selected" + :title="i18n.clearButton.title" + :aria-label="i18n.clearButton.ariaLabel" @click="clearSelection" > diff --git a/src/locales/en.js b/src/locales/en.js new file mode 100644 index 0000000..3e1ae42 --- /dev/null +++ b/src/locales/en.js @@ -0,0 +1,19 @@ +export const locale = { + spinner: { + text: 'Loading...', + }, + noOptions: { + text: 'Sorry, no matching options.', + }, + search: { + ariaLabel: 'Search for options', + }, + deselectButton: { + ariaLabel: (label) => `Deselect ${label}`, + title: (label) => `Deselect ${label}`, + }, + clearButton: { + ariaLabel: 'Clear Selection', + title: 'Clear Selection', + }, +} diff --git a/src/locales/es.js b/src/locales/es.js new file mode 100644 index 0000000..2ad6fe6 --- /dev/null +++ b/src/locales/es.js @@ -0,0 +1,19 @@ +export const locale = { + spinner: { + text: 'Cargando...', + }, + noOptions: { + text: 'Lo sentimos, no hay opciones que coincidan.', + }, + search: { + ariaLabel: 'Buscar opciones', + }, + deselectButton: { + ariaLabel: (label) => `Deseleccionar ${label}`, + title: (label) => `Deseleccionar ${label}`, + }, + clearButton: { + ariaLabel: 'Eliminar selección', + title: 'Eliminar selección', + }, +} diff --git a/src/locales/fr.js b/src/locales/fr.js new file mode 100644 index 0000000..b3beb21 --- /dev/null +++ b/src/locales/fr.js @@ -0,0 +1,19 @@ +export const locale = { + spinner: { + text: 'Chargement...', + }, + noOptions: { + text: 'Désolé, aucune option correspondante.', + }, + search: { + ariaLabel: 'Rechercher des options', + }, + deselectButton: { + ariaLabel: (label) => `Désélectionner ${label}`, + title: (label) => `Désélectionner ${label}`, + }, + clearButton: { + ariaLabel: 'Effacer la sélection', + title: 'Effacer la sélection', + }, +} diff --git a/src/mixins/i18n.js b/src/mixins/i18n.js index a6857f1..e2d5122 100644 --- a/src/mixins/i18n.js +++ b/src/mixins/i18n.js @@ -1,25 +1,4 @@ -const deselectLabel = (label) => `Deselect ${label}` - -export const text = { - spinner: { - text: 'Loading...', - }, - noOptions: { - text: 'Sorry, no matching options.', - }, - search: { - ariaLabel: 'Search for options', - }, - selectedOption: {}, - deselectButton: { - ariaLabel: deselectLabel, - title: deselectLabel, - }, - clearButton: { - ariaLabel: 'Clear Selection', - title: 'Clear Selection', - }, -} +import { locale as english } from '../locales/en.js' /** * @this VueSelect @@ -27,18 +6,18 @@ export const text = { */ export default { props: { - text: { + locale: { type: Function, /** * @param text {Object} - * @return {*} + * @return {Object} */ default: (text) => text, }, }, computed: { i18n() { - return this.text(text) + return this.locale(english) }, }, }