Make language packs load async (#204)
* Webpack magic to make language packs async * Fix jest failing * Ensure the native language names are always present * Add loading state for language packspull/206/head
parent
9b0a7dc4ac
commit
de76ad9a43
|
@ -25,6 +25,7 @@ THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import common from './common';
|
import common from './common';
|
||||||
|
import languages from './languages';
|
||||||
import templates from './templates';
|
import templates from './templates';
|
||||||
|
|
||||||
export default { common, templates };
|
export default { common, languages, templates };
|
||||||
|
|
|
@ -24,7 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { default as en } from './en';
|
export default {
|
||||||
export { default as zhCN } from './zh-cn';
|
en: 'English',
|
||||||
export { default as zhTW } from './zh-tw';
|
zhCN: 'Chinese (simplified)',
|
||||||
export { default as ptBR } from './pt-br';
|
zhTW: 'Chinese (traditional)',
|
||||||
|
ptBR: 'Portuguese (brazilian)',
|
||||||
|
};
|
|
@ -29,10 +29,6 @@ import common from '../common';
|
||||||
export default {
|
export default {
|
||||||
title: `${common.nginx}Config`,
|
title: `${common.nginx}Config`,
|
||||||
description: `The easiest way to configure a performant, secure, and stable ${common.nginx} server.`,
|
description: `The easiest way to configure a performant, secure, and stable ${common.nginx} server.`,
|
||||||
en: 'English',
|
|
||||||
zhCN: 'Chinese (simplified)',
|
|
||||||
zhTW: 'Chinese (traditional)',
|
|
||||||
ptBR: 'Portuguese (brazilian)',
|
|
||||||
singleColumnMode: 'Single column mode',
|
singleColumnMode: 'Single column mode',
|
||||||
splitColumnMode: 'Split column mode',
|
splitColumnMode: 'Split column mode',
|
||||||
perWebsiteConfig: 'Per-website config',
|
perWebsiteConfig: 'Per-website config',
|
||||||
|
|
|
@ -25,6 +25,7 @@ THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import common from './common';
|
import common from './common';
|
||||||
|
import languages from './languages';
|
||||||
import templates from './templates';
|
import templates from './templates';
|
||||||
|
|
||||||
export default { common, templates };
|
export default { common, languages, templates };
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 DigitalOcean
|
||||||
|
|
||||||
|
This code is licensed under the MIT License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
https://github.com/digitalocean/nginxconfig.io/blob/master/LICENSE or https://mit-license.org/
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
en: 'Inglês',
|
||||||
|
zhCN: 'Chinês (simplificado)',
|
||||||
|
zhTW: 'Chinês (tradicional)',
|
||||||
|
ptBR: 'Português (brasileiro)',
|
||||||
|
};
|
|
@ -29,10 +29,6 @@ import common from '../common';
|
||||||
export default {
|
export default {
|
||||||
title: `${common.nginx}Configuração`,
|
title: `${common.nginx}Configuração`,
|
||||||
description: `A maneira mais fácil de configurar um servidor ${common.nginx} de alto desempenho, seguro e estável.`,
|
description: `A maneira mais fácil de configurar um servidor ${common.nginx} de alto desempenho, seguro e estável.`,
|
||||||
en: 'Inglês',
|
|
||||||
zhCN: 'Chinês (simplificado)',
|
|
||||||
zhTW: 'Chinês (tradicional)',
|
|
||||||
ptBR: 'Português (brasileiro)',
|
|
||||||
singleColumnMode: 'Modo de coluna única',
|
singleColumnMode: 'Modo de coluna única',
|
||||||
splitColumnMode: 'Modo com divisão de colunas',
|
splitColumnMode: 'Modo com divisão de colunas',
|
||||||
perWebsiteConfig: 'Configuração por site',
|
perWebsiteConfig: 'Configuração por site',
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 DigitalOcean
|
||||||
|
|
||||||
|
This code is licensed under the MIT License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
https://github.com/digitalocean/nginxconfig.io/blob/master/LICENSE or https://mit-license.org/
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Vue from 'vue';
|
||||||
|
import VueI18n from 'vue-i18n';
|
||||||
|
import { defaultPack, defaultPackData } from '../util/language_pack_default';
|
||||||
|
import { toSep } from '../util/language_pack_name';
|
||||||
|
import { languagePackContext, availablePacks } from '../util/language_pack_context';
|
||||||
|
|
||||||
|
Vue.use(VueI18n);
|
||||||
|
|
||||||
|
// Load in the full default pack
|
||||||
|
const i18nPacks = {};
|
||||||
|
i18nPacks[defaultPack] = defaultPackData;
|
||||||
|
const loadedI18nPacks = [defaultPack];
|
||||||
|
|
||||||
|
// Load in languages data from other packs
|
||||||
|
// Use webpack magic to only build chunks for lang/languages.js
|
||||||
|
const languagesContext = require.context('.', true, /^\.\/[^/]+\/languages\.js$/, 'sync');
|
||||||
|
for (const availablePack of availablePacks) {
|
||||||
|
if (availablePack === defaultPack) continue;
|
||||||
|
i18nPacks[availablePack] = { languages: languagesContext(`./${toSep(availablePack, '-')}/languages.js`).default };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const i18n = new VueI18n({
|
||||||
|
locale: defaultPack,
|
||||||
|
fallbackLocale: defaultPack,
|
||||||
|
messages: i18nPacks,
|
||||||
|
});
|
||||||
|
|
||||||
|
const loadLanguagePack = pack => {
|
||||||
|
// If same language, do nothing
|
||||||
|
if (i18n.locale === pack) return;
|
||||||
|
|
||||||
|
// If language already loaded, do nothing
|
||||||
|
if (loadedI18nPacks.includes(pack)) return;
|
||||||
|
|
||||||
|
// Load the pack with webpack magic
|
||||||
|
return languagePackContext(`./${toSep(pack, '-')}/index.js`).then(packData => i18nPacks[pack] = packData.default);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setLanguagePack = async pack => {
|
||||||
|
await loadLanguagePack(pack);
|
||||||
|
i18n.locale = pack;
|
||||||
|
};
|
|
@ -24,9 +24,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { readdirSync } from 'fs';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import defaultPack from './default';
|
import { defaultPack } from '../util/language_pack_default';
|
||||||
import * as packs from './index';
|
import { fromSep } from '../util/language_pack_name';
|
||||||
|
|
||||||
|
// Load all the packs in
|
||||||
|
const packs = {};
|
||||||
|
const packDirectories = readdirSync(__dirname, { withFileTypes: true })
|
||||||
|
.filter(dirent => dirent.isDirectory())
|
||||||
|
.map(dirent => dirent.name);
|
||||||
|
for (const packDirectory of packDirectories)
|
||||||
|
packs[fromSep(packDirectory, '-')] = require(`./${packDirectory}/index.js`).default;
|
||||||
|
|
||||||
// Recursively get all keys in a i18n pack object fragment
|
// Recursively get all keys in a i18n pack object fragment
|
||||||
const explore = packFragment => {
|
const explore = packFragment => {
|
||||||
|
|
|
@ -25,6 +25,7 @@ THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import common from './common';
|
import common from './common';
|
||||||
|
import languages from './languages';
|
||||||
import templates from './templates';
|
import templates from './templates';
|
||||||
|
|
||||||
export default { common, templates };
|
export default { common, languages, templates };
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 DigitalOcean
|
||||||
|
|
||||||
|
This code is licensed under the MIT License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
https://github.com/digitalocean/nginxconfig.io/blob/master/LICENSE or https://mit-license.org/
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
en: '英语',
|
||||||
|
zhCN: '简体中文',
|
||||||
|
zhTW: '繁体中文',
|
||||||
|
ptBR: '葡萄牙语 (巴西)',
|
||||||
|
};
|
|
@ -29,10 +29,6 @@ import common from '../common';
|
||||||
export default {
|
export default {
|
||||||
title: `${common.nginx} 配置`,
|
title: `${common.nginx} 配置`,
|
||||||
description: `配置高性能、安全、稳定的${common.nginx}服务器的最简单方法。`,
|
description: `配置高性能、安全、稳定的${common.nginx}服务器的最简单方法。`,
|
||||||
en: '英语',
|
|
||||||
zhCN: '简体中文',
|
|
||||||
zhTW: '繁体中文',
|
|
||||||
ptBR: '葡萄牙语 (巴西)',
|
|
||||||
singleColumnMode: '垂直模式',
|
singleColumnMode: '垂直模式',
|
||||||
splitColumnMode: '水平模式',
|
splitColumnMode: '水平模式',
|
||||||
perWebsiteConfig: '站点配置',
|
perWebsiteConfig: '站点配置',
|
||||||
|
|
|
@ -25,6 +25,7 @@ THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import common from './common';
|
import common from './common';
|
||||||
|
import languages from './languages';
|
||||||
import templates from './templates';
|
import templates from './templates';
|
||||||
|
|
||||||
export default { common, templates };
|
export default { common, languages, templates };
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 DigitalOcean
|
||||||
|
|
||||||
|
This code is licensed under the MIT License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
https://github.com/digitalocean/nginxconfig.io/blob/master/LICENSE or https://mit-license.org/
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
en: '英語',
|
||||||
|
zhCN: '簡體中文',
|
||||||
|
zhTW: '繁體中文',
|
||||||
|
ptBR: '葡萄牙語(巴西)',
|
||||||
|
};
|
|
@ -29,10 +29,6 @@ import common from '../common';
|
||||||
export default {
|
export default {
|
||||||
title: `${common.nginx} 配寘`,
|
title: `${common.nginx} 配寘`,
|
||||||
description: `配寘高性能、安全、穩定的${common.nginx}服務器的最簡單方法。`,
|
description: `配寘高性能、安全、穩定的${common.nginx}服務器的最簡單方法。`,
|
||||||
en: '英語',
|
|
||||||
zhCN: '簡體中文',
|
|
||||||
zhTW: '繁體中文',
|
|
||||||
ptBR: '葡萄牙語(巴西)',
|
|
||||||
singleColumnMode: '垂直模式',
|
singleColumnMode: '垂直模式',
|
||||||
splitColumnMode: '水准模式',
|
splitColumnMode: '水准模式',
|
||||||
perWebsiteConfig: '網站配寘',
|
perWebsiteConfig: '網站配寘',
|
||||||
|
|
|
@ -26,19 +26,9 @@ THE SOFTWARE.
|
||||||
|
|
||||||
import './scss/style.scss';
|
import './scss/style.scss';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import VueI18n from 'vue-i18n';
|
|
||||||
import './util/prism_bundle';
|
import './util/prism_bundle';
|
||||||
|
import { i18n } from './i18n/setup';
|
||||||
import App from './templates/app';
|
import App from './templates/app';
|
||||||
import * as i18nPacks from './i18n';
|
|
||||||
import i18nDefault from './i18n/default';
|
|
||||||
|
|
||||||
Vue.use(VueI18n);
|
|
||||||
|
|
||||||
const i18n = new VueI18n({
|
|
||||||
locale: i18nDefault,
|
|
||||||
fallbackLocale: i18nDefault,
|
|
||||||
messages: i18nPacks,
|
|
||||||
});
|
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
i18n,
|
i18n,
|
||||||
|
|
|
@ -37,10 +37,12 @@ THE SOFTWARE.
|
||||||
:options="i18nPacks"
|
:options="i18nPacks"
|
||||||
:clearable="false"
|
:clearable="false"
|
||||||
:reduce="s => s.value"
|
:reduce="s => s.value"
|
||||||
|
:disabled="languageLoading"
|
||||||
>
|
>
|
||||||
<template #selected-option="{ label }">
|
<template #selected-option="{ label }">
|
||||||
<span class="has-icon">
|
<span class="has-icon">
|
||||||
<i class="icon fas fa-language"></i>
|
<i v-if="languageLoading" class="icon fas fa-spinner fa-pulse"></i>
|
||||||
|
<i v-else class="icon fas fa-language"></i>
|
||||||
<span>{{ label }}</span>
|
<span>{{ label }}</span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
@ -123,9 +125,11 @@ THE SOFTWARE.
|
||||||
import isObject from '../util/is_object';
|
import isObject from '../util/is_object';
|
||||||
import analytics from '../util/analytics';
|
import analytics from '../util/analytics';
|
||||||
import browserLanguage from '../util/browser_language';
|
import browserLanguage from '../util/browser_language';
|
||||||
|
import { toSep } from '../util/language_pack_name';
|
||||||
|
import { defaultPack } from '../util/language_pack_default';
|
||||||
|
import { availablePacks } from '../util/language_pack_context';
|
||||||
|
|
||||||
import * as i18nPacks from '../i18n';
|
import { setLanguagePack } from '../i18n/setup';
|
||||||
import i18nDefault from '../i18n/default';
|
|
||||||
import generators from '../generators';
|
import generators from '../generators';
|
||||||
|
|
||||||
import Domain from './domain';
|
import Domain from './domain';
|
||||||
|
@ -155,9 +159,9 @@ THE SOFTWARE.
|
||||||
...Global.delegated,
|
...Global.delegated,
|
||||||
app: {
|
app: {
|
||||||
lang: {
|
lang: {
|
||||||
default: i18nDefault,
|
default: defaultPack,
|
||||||
value: i18nDefault,
|
value: defaultPack,
|
||||||
computed: i18nDefault,
|
computed: defaultPack,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -168,6 +172,8 @@ THE SOFTWARE.
|
||||||
confWatcherWaiting: false,
|
confWatcherWaiting: false,
|
||||||
confFilesPrevious: {},
|
confFilesPrevious: {},
|
||||||
confFilesOutput: {},
|
confFilesOutput: {},
|
||||||
|
languageLoading: false,
|
||||||
|
languagePrevious: defaultPack,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -187,10 +193,10 @@ THE SOFTWARE.
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
i18nPacks() {
|
i18nPacks() {
|
||||||
return Object.keys(i18nPacks).map(pack => ({
|
return availablePacks.map(pack => ({
|
||||||
label: this.$t(`templates.app.${pack}`) + (pack === this.$i18n.locale
|
label: this.$t(`languages.${pack}`) + (pack === this.$i18n.locale
|
||||||
? ''
|
? ''
|
||||||
: ` - ${this.$t(`templates.app.${pack}`, pack)}`),
|
: ` - ${this.$t(`languages.${pack}`, pack)}`),
|
||||||
value: pack,
|
value: pack,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
@ -208,16 +214,30 @@ THE SOFTWARE.
|
||||||
},
|
},
|
||||||
'$data.global.app.lang': {
|
'$data.global.app.lang': {
|
||||||
handler(data) {
|
handler(data) {
|
||||||
|
this.$data.languageLoading = true;
|
||||||
|
|
||||||
// Ensure valid pack
|
// Ensure valid pack
|
||||||
if (!(data.value in i18nPacks)) data.computed = data.default;
|
if (!availablePacks.includes(data.value)) data.computed = data.default;
|
||||||
|
|
||||||
// Update the locale
|
// Update the locale
|
||||||
this.$i18n.locale = data.computed;
|
setLanguagePack(data.computed).then(() => {
|
||||||
|
// Done
|
||||||
|
console.log('Language set to', data.computed);
|
||||||
|
this.$data.languagePrevious = data.computed;
|
||||||
|
this.$data.languageLoading = false;
|
||||||
|
|
||||||
// Analytics
|
// Analytics
|
||||||
const pack = data.computed.match(/^([a-z]+)([A-Z]*)$/).slice(1)
|
analytics(`set_language_${toSep(data.computed, '_')}`, 'Language');
|
||||||
.map(x => x.toLowerCase()).filter(x => !!x).join('_');
|
}).catch((err) => {
|
||||||
analytics(`set_language_${pack}`, 'Language');
|
// Error
|
||||||
|
console.log('Failed to set language to', data.computed);
|
||||||
|
console.error(err);
|
||||||
|
|
||||||
|
// Fallback to last known good
|
||||||
|
data.value = this.$data.languagePrevious;
|
||||||
|
data.computed = this.$data.languagePrevious;
|
||||||
|
this.$data.languageLoading = false;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
deep: true,
|
deep: true,
|
||||||
},
|
},
|
||||||
|
@ -231,7 +251,7 @@ THE SOFTWARE.
|
||||||
|
|
||||||
// Apply browser language if not specified in query
|
// Apply browser language if not specified in query
|
||||||
if (!imported || !imported.global || !imported.global.app || !imported.global.app.lang) {
|
if (!imported || !imported.global || !imported.global.app || !imported.global.app.lang) {
|
||||||
const language = browserLanguage();
|
const language = browserLanguage(availablePacks);
|
||||||
if (language) this.lang = language;
|
if (language) this.lang = language;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as i18nPacks from '../i18n';
|
import { fromSep } from './language_pack_name';
|
||||||
|
|
||||||
const toPack = locale => locale.split('-', 2)[0].toLowerCase() + (locale.split('-', 2)[1] || '').toUpperCase();
|
export default availablePacks => {
|
||||||
|
|
||||||
export default () => {
|
|
||||||
if (typeof window === 'object' && typeof window.navigator === 'object') {
|
if (typeof window === 'object' && typeof window.navigator === 'object') {
|
||||||
const userLocales = new Set();
|
const userLocales = new Set();
|
||||||
|
|
||||||
|
@ -42,12 +40,11 @@ export default () => {
|
||||||
userLocales.add(Intl.DateTimeFormat().resolvedOptions().locale);
|
userLocales.add(Intl.DateTimeFormat().resolvedOptions().locale);
|
||||||
|
|
||||||
// Try to find an exact region/language match
|
// Try to find an exact region/language match
|
||||||
const i18nPackLocales = Object.keys(i18nPacks);
|
const exactMatch = [...userLocales.values()].find(locale => availablePacks.includes(fromSep(locale, '-')));
|
||||||
const exactMatch = [...userLocales.values()].find(locale => i18nPackLocales.includes(toPack(locale)));
|
if (exactMatch) return fromSep(exactMatch, '-');
|
||||||
if (exactMatch) return toPack(exactMatch);
|
|
||||||
|
|
||||||
// Build a map of languages to pack
|
// Build a map of languages to pack
|
||||||
const i18nPackLanguages = i18nPackLocales.reduce((map, pack) => {
|
const i18nPackLanguages = availablePacks.reduce((map, pack) => {
|
||||||
const lang = pack.match(/^[a-z]+/)[0];
|
const lang = pack.match(/^[a-z]+/)[0];
|
||||||
if (!(lang in map)) map[lang] = pack;
|
if (!(lang in map)) map[lang] = pack;
|
||||||
return map;
|
return map;
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 DigitalOcean
|
||||||
|
|
||||||
|
This code is licensed under the MIT License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
https://github.com/digitalocean/nginxconfig.io/blob/master/LICENSE or https://mit-license.org/
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { fromSep } from './language_pack_name';
|
||||||
|
|
||||||
|
// Use webpack magic to only build chunks for lang/index.js, not subdirectories (e.g. lang/templates/index.js)
|
||||||
|
export const languagePackContext = require.context('../i18n', true, /^\.\/[^/]+\/index\.js$/, 'lazy');
|
||||||
|
|
||||||
|
// Webpack magic to get all the packs that are available
|
||||||
|
export const availablePacks = Object.freeze(languagePackContext
|
||||||
|
.keys()
|
||||||
|
.map(pack => pack.match(/^\.\/([^/]+)\/index\.js$/))
|
||||||
|
.filter(pack => pack !== null)
|
||||||
|
.map(pack => fromSep(pack[1], '-')));
|
|
@ -24,4 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default 'en';
|
|
||||||
|
export const defaultPack = 'en';
|
||||||
|
|
||||||
|
export { default as defaultPackData } from '../i18n/en';
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 DigitalOcean
|
||||||
|
|
||||||
|
This code is licensed under the MIT License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
https://github.com/digitalocean/nginxconfig.io/blob/master/LICENSE or https://mit-license.org/
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const toSep = (pack, sep) => pack
|
||||||
|
.match(/^([a-z]+)([A-Z]*)$/)
|
||||||
|
.slice(1)
|
||||||
|
.map(x => x.toLowerCase())
|
||||||
|
.filter(x => !!x)
|
||||||
|
.join(sep);
|
||||||
|
|
||||||
|
export const fromSep = (pack, sep) => pack.split(sep, 2)[0].toLowerCase() + (pack.split(sep, 2)[1] || '').toUpperCase();
|
|
@ -46,7 +46,6 @@ class MockLocales {
|
||||||
}
|
}
|
||||||
|
|
||||||
static setDateTimeLocale(locale) {
|
static setDateTimeLocale(locale) {
|
||||||
const newDateTimeFormat = new Intl.DateTimeFormat(locale);
|
|
||||||
MockLocales.IntlBackup = Intl;
|
MockLocales.IntlBackup = Intl;
|
||||||
if (!locale) {
|
if (!locale) {
|
||||||
// eslint-disable-next-line no-global-assign
|
// eslint-disable-next-line no-global-assign
|
||||||
|
@ -54,6 +53,7 @@ class MockLocales {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const newDateTimeFormat = new Intl.DateTimeFormat(locale);
|
||||||
// eslint-disable-next-line no-global-assign
|
// eslint-disable-next-line no-global-assign
|
||||||
Intl = {
|
Intl = {
|
||||||
DateTimeFormat() {
|
DateTimeFormat() {
|
||||||
|
@ -97,13 +97,13 @@ describe('browserLanguage', () => {
|
||||||
MockLocales.setDateTimeLocale(undefined);
|
MockLocales.setDateTimeLocale(undefined);
|
||||||
|
|
||||||
MockLocales.setNavigatorLanguages(['zh-CN', 'zh','en-US','en']);
|
MockLocales.setNavigatorLanguages(['zh-CN', 'zh','en-US','en']);
|
||||||
expect(browserLanguage()).toEqual('zhCN');
|
expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toEqual('zhCN');
|
||||||
|
|
||||||
MockLocales.setNavigatorLanguages(['zh-TW','zh','en-US','en']);
|
MockLocales.setNavigatorLanguages(['zh-TW','zh','en-US','en']);
|
||||||
expect(browserLanguage()).toEqual('zhTW');
|
expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toEqual('zhTW');
|
||||||
|
|
||||||
MockLocales.setNavigatorLanguages(['zh', 'en-US', 'en']);
|
MockLocales.setNavigatorLanguages(['zh', 'en-US', 'en']);
|
||||||
expect(browserLanguage()).toEqual('en');
|
expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toEqual('en');
|
||||||
|
|
||||||
MockLocales.restoreDateTimeLocale();
|
MockLocales.restoreDateTimeLocale();
|
||||||
});
|
});
|
||||||
|
@ -112,7 +112,7 @@ describe('browserLanguage', () => {
|
||||||
MockLocales.setDateTimeLocale(undefined);
|
MockLocales.setDateTimeLocale(undefined);
|
||||||
|
|
||||||
MockLocales.setNavigatorLanguages(['ja-JP', 'ja', 'en-US']);
|
MockLocales.setNavigatorLanguages(['ja-JP', 'ja', 'en-US']);
|
||||||
expect(browserLanguage()).toEqual('en');
|
expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toEqual('en');
|
||||||
|
|
||||||
MockLocales.restoreDateTimeLocale();
|
MockLocales.restoreDateTimeLocale();
|
||||||
});
|
});
|
||||||
|
@ -121,7 +121,7 @@ describe('browserLanguage', () => {
|
||||||
MockLocales.setDateTimeLocale(undefined);
|
MockLocales.setDateTimeLocale(undefined);
|
||||||
|
|
||||||
MockLocales.setNavigatorLanguages(['ja-JP', 'ja', 'zh']);
|
MockLocales.setNavigatorLanguages(['ja-JP', 'ja', 'zh']);
|
||||||
expect(browserLanguage()).toEqual('zhCN');
|
expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toEqual('zhCN');
|
||||||
|
|
||||||
MockLocales.restoreDateTimeLocale();
|
MockLocales.restoreDateTimeLocale();
|
||||||
});
|
});
|
||||||
|
@ -130,7 +130,7 @@ describe('browserLanguage', () => {
|
||||||
MockLocales.setDateTimeLocale(undefined);
|
MockLocales.setDateTimeLocale(undefined);
|
||||||
|
|
||||||
MockLocales.setNavigatorLanguages(['ja-JP','ja']);
|
MockLocales.setNavigatorLanguages(['ja-JP','ja']);
|
||||||
expect(browserLanguage()).toBeFalsy();
|
expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toBeFalsy();
|
||||||
|
|
||||||
MockLocales.restoreDateTimeLocale();
|
MockLocales.restoreDateTimeLocale();
|
||||||
});
|
});
|
||||||
|
@ -140,7 +140,7 @@ describe('browserLanguage', () => {
|
||||||
MockLocales.setNavigatorLanguages(undefined);
|
MockLocales.setNavigatorLanguages(undefined);
|
||||||
MockLocales.setNavigatorLanguage(undefined);
|
MockLocales.setNavigatorLanguage(undefined);
|
||||||
MockLocales.setDateTimeLocale(undefined);
|
MockLocales.setDateTimeLocale(undefined);
|
||||||
expect(browserLanguage()).toBeFalsy();
|
expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toBeFalsy();
|
||||||
MockLocales.restoreDateTimeLocale();
|
MockLocales.restoreDateTimeLocale();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ describe('browserLanguage', () => {
|
||||||
MockLocales.setNavigatorLanguage('en');
|
MockLocales.setNavigatorLanguage('en');
|
||||||
MockLocales.setNavigatorLanguages(undefined);
|
MockLocales.setNavigatorLanguages(undefined);
|
||||||
MockLocales.setDateTimeLocale(undefined);
|
MockLocales.setDateTimeLocale(undefined);
|
||||||
expect(browserLanguage()).toEqual('en');
|
expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toEqual('en');
|
||||||
MockLocales.restoreDateTimeLocale();
|
MockLocales.restoreDateTimeLocale();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -156,14 +156,14 @@ describe('browserLanguage', () => {
|
||||||
MockLocales.setNavigatorLanguage(undefined);
|
MockLocales.setNavigatorLanguage(undefined);
|
||||||
MockLocales.setNavigatorLanguages(['en-US','en']);
|
MockLocales.setNavigatorLanguages(['en-US','en']);
|
||||||
MockLocales.setDateTimeLocale(undefined);
|
MockLocales.setDateTimeLocale(undefined);
|
||||||
expect(browserLanguage()).toEqual('en');
|
expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toEqual('en');
|
||||||
MockLocales.restoreDateTimeLocale();
|
MockLocales.restoreDateTimeLocale();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('navigator is `undefined` and Intl locale is `en-US`',() => {
|
test('navigator is `undefined` and Intl locale is `en-US`',() => {
|
||||||
MockLocales.setNavigator(undefined);
|
MockLocales.setNavigator(undefined);
|
||||||
MockLocales.setDateTimeLocale('en-US');
|
MockLocales.setDateTimeLocale('en-US');
|
||||||
expect(browserLanguage()).toEqual('en');
|
expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toEqual('en');
|
||||||
MockLocales.restoreDateTimeLocale();
|
MockLocales.restoreDateTimeLocale();
|
||||||
MockLocales.restoreNavigator();
|
MockLocales.restoreNavigator();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue