From de76ad9a4318c12d9f20c411720b7a18b7d19bd0 Mon Sep 17 00:00:00 2001 From: "Matt (IPv4) Cowley" Date: Tue, 29 Dec 2020 16:46:58 +0000 Subject: [PATCH] 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 packs --- src/nginxconfig/i18n/en/index.js | 3 +- .../i18n/{index.js => en/languages.js} | 10 +-- src/nginxconfig/i18n/en/templates/app.js | 4 -- src/nginxconfig/i18n/pt-br/index.js | 3 +- src/nginxconfig/i18n/pt-br/languages.js | 32 +++++++++ src/nginxconfig/i18n/pt-br/templates/app.js | 4 -- src/nginxconfig/i18n/setup.js | 68 +++++++++++++++++++ src/nginxconfig/i18n/verify.js | 13 +++- src/nginxconfig/i18n/zh-cn/index.js | 3 +- src/nginxconfig/i18n/zh-cn/languages.js | 32 +++++++++ src/nginxconfig/i18n/zh-cn/templates/app.js | 4 -- src/nginxconfig/i18n/zh-tw/index.js | 3 +- src/nginxconfig/i18n/zh-tw/languages.js | 32 +++++++++ src/nginxconfig/i18n/zh-tw/templates/app.js | 4 -- src/nginxconfig/mount.js | 12 +--- src/nginxconfig/templates/app.vue | 54 ++++++++++----- src/nginxconfig/util/browser_language.js | 13 ++-- src/nginxconfig/util/language_pack_context.js | 37 ++++++++++ .../language_pack_default.js} | 5 +- src/nginxconfig/util/language_pack_name.js | 34 ++++++++++ test/testBrowserLanguage.js | 22 +++--- 21 files changed, 318 insertions(+), 74 deletions(-) rename src/nginxconfig/i18n/{index.js => en/languages.js} (88%) create mode 100644 src/nginxconfig/i18n/pt-br/languages.js create mode 100644 src/nginxconfig/i18n/setup.js create mode 100644 src/nginxconfig/i18n/zh-cn/languages.js create mode 100644 src/nginxconfig/i18n/zh-tw/languages.js create mode 100644 src/nginxconfig/util/language_pack_context.js rename src/nginxconfig/{i18n/default.js => util/language_pack_default.js} (93%) create mode 100644 src/nginxconfig/util/language_pack_name.js diff --git a/src/nginxconfig/i18n/en/index.js b/src/nginxconfig/i18n/en/index.js index 4cb86a6..01c3f3e 100644 --- a/src/nginxconfig/i18n/en/index.js +++ b/src/nginxconfig/i18n/en/index.js @@ -25,6 +25,7 @@ THE SOFTWARE. */ import common from './common'; +import languages from './languages'; import templates from './templates'; -export default { common, templates }; +export default { common, languages, templates }; diff --git a/src/nginxconfig/i18n/index.js b/src/nginxconfig/i18n/en/languages.js similarity index 88% rename from src/nginxconfig/i18n/index.js rename to src/nginxconfig/i18n/en/languages.js index 68df2e8..d98f1ac 100644 --- a/src/nginxconfig/i18n/index.js +++ b/src/nginxconfig/i18n/en/languages.js @@ -24,7 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -export { default as en } from './en'; -export { default as zhCN } from './zh-cn'; -export { default as zhTW } from './zh-tw'; -export { default as ptBR } from './pt-br'; +export default { + en: 'English', + zhCN: 'Chinese (simplified)', + zhTW: 'Chinese (traditional)', + ptBR: 'Portuguese (brazilian)', +}; diff --git a/src/nginxconfig/i18n/en/templates/app.js b/src/nginxconfig/i18n/en/templates/app.js index 3901860..f170c89 100644 --- a/src/nginxconfig/i18n/en/templates/app.js +++ b/src/nginxconfig/i18n/en/templates/app.js @@ -29,10 +29,6 @@ import common from '../common'; export default { title: `${common.nginx}Config`, 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', splitColumnMode: 'Split column mode', perWebsiteConfig: 'Per-website config', diff --git a/src/nginxconfig/i18n/pt-br/index.js b/src/nginxconfig/i18n/pt-br/index.js index 4cb86a6..01c3f3e 100644 --- a/src/nginxconfig/i18n/pt-br/index.js +++ b/src/nginxconfig/i18n/pt-br/index.js @@ -25,6 +25,7 @@ THE SOFTWARE. */ import common from './common'; +import languages from './languages'; import templates from './templates'; -export default { common, templates }; +export default { common, languages, templates }; diff --git a/src/nginxconfig/i18n/pt-br/languages.js b/src/nginxconfig/i18n/pt-br/languages.js new file mode 100644 index 0000000..a3ce695 --- /dev/null +++ b/src/nginxconfig/i18n/pt-br/languages.js @@ -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)', +}; diff --git a/src/nginxconfig/i18n/pt-br/templates/app.js b/src/nginxconfig/i18n/pt-br/templates/app.js index 63dd9fd..627a0a4 100644 --- a/src/nginxconfig/i18n/pt-br/templates/app.js +++ b/src/nginxconfig/i18n/pt-br/templates/app.js @@ -29,10 +29,6 @@ import common from '../common'; export default { title: `${common.nginx}Configuração`, 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', splitColumnMode: 'Modo com divisão de colunas', perWebsiteConfig: 'Configuração por site', diff --git a/src/nginxconfig/i18n/setup.js b/src/nginxconfig/i18n/setup.js new file mode 100644 index 0000000..7b807bb --- /dev/null +++ b/src/nginxconfig/i18n/setup.js @@ -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; +}; diff --git a/src/nginxconfig/i18n/verify.js b/src/nginxconfig/i18n/verify.js index c1e3d5d..3fbc254 100644 --- a/src/nginxconfig/i18n/verify.js +++ b/src/nginxconfig/i18n/verify.js @@ -24,9 +24,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import { readdirSync } from 'fs'; import chalk from 'chalk'; -import defaultPack from './default'; -import * as packs from './index'; +import { defaultPack } from '../util/language_pack_default'; +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 const explore = packFragment => { diff --git a/src/nginxconfig/i18n/zh-cn/index.js b/src/nginxconfig/i18n/zh-cn/index.js index 4cb86a6..01c3f3e 100644 --- a/src/nginxconfig/i18n/zh-cn/index.js +++ b/src/nginxconfig/i18n/zh-cn/index.js @@ -25,6 +25,7 @@ THE SOFTWARE. */ import common from './common'; +import languages from './languages'; import templates from './templates'; -export default { common, templates }; +export default { common, languages, templates }; diff --git a/src/nginxconfig/i18n/zh-cn/languages.js b/src/nginxconfig/i18n/zh-cn/languages.js new file mode 100644 index 0000000..f12ad00 --- /dev/null +++ b/src/nginxconfig/i18n/zh-cn/languages.js @@ -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: '葡萄牙语 (巴西)', +}; diff --git a/src/nginxconfig/i18n/zh-cn/templates/app.js b/src/nginxconfig/i18n/zh-cn/templates/app.js index a823fc4..7c382f0 100644 --- a/src/nginxconfig/i18n/zh-cn/templates/app.js +++ b/src/nginxconfig/i18n/zh-cn/templates/app.js @@ -29,10 +29,6 @@ import common from '../common'; export default { title: `${common.nginx} 配置`, description: `配置高性能、安全、稳定的${common.nginx}服务器的最简单方法。`, - en: '英语', - zhCN: '简体中文', - zhTW: '繁体中文', - ptBR: '葡萄牙语 (巴西)', singleColumnMode: '垂直模式', splitColumnMode: '水平模式', perWebsiteConfig: '站点配置', diff --git a/src/nginxconfig/i18n/zh-tw/index.js b/src/nginxconfig/i18n/zh-tw/index.js index 4cb86a6..01c3f3e 100644 --- a/src/nginxconfig/i18n/zh-tw/index.js +++ b/src/nginxconfig/i18n/zh-tw/index.js @@ -25,6 +25,7 @@ THE SOFTWARE. */ import common from './common'; +import languages from './languages'; import templates from './templates'; -export default { common, templates }; +export default { common, languages, templates }; diff --git a/src/nginxconfig/i18n/zh-tw/languages.js b/src/nginxconfig/i18n/zh-tw/languages.js new file mode 100644 index 0000000..bff5b47 --- /dev/null +++ b/src/nginxconfig/i18n/zh-tw/languages.js @@ -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: '葡萄牙語(巴西)', +}; diff --git a/src/nginxconfig/i18n/zh-tw/templates/app.js b/src/nginxconfig/i18n/zh-tw/templates/app.js index c244ec5..6989ca5 100644 --- a/src/nginxconfig/i18n/zh-tw/templates/app.js +++ b/src/nginxconfig/i18n/zh-tw/templates/app.js @@ -29,10 +29,6 @@ import common from '../common'; export default { title: `${common.nginx} 配寘`, description: `配寘高性能、安全、穩定的${common.nginx}服務器的最簡單方法。`, - en: '英語', - zhCN: '簡體中文', - zhTW: '繁體中文', - ptBR: '葡萄牙語(巴西)', singleColumnMode: '垂直模式', splitColumnMode: '水准模式', perWebsiteConfig: '網站配寘', diff --git a/src/nginxconfig/mount.js b/src/nginxconfig/mount.js index 9522006..dd0aadb 100644 --- a/src/nginxconfig/mount.js +++ b/src/nginxconfig/mount.js @@ -26,19 +26,9 @@ THE SOFTWARE. import './scss/style.scss'; import Vue from 'vue'; -import VueI18n from 'vue-i18n'; import './util/prism_bundle'; +import { i18n } from './i18n/setup'; 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({ i18n, diff --git a/src/nginxconfig/templates/app.vue b/src/nginxconfig/templates/app.vue index f4b360a..76874f1 100644 --- a/src/nginxconfig/templates/app.vue +++ b/src/nginxconfig/templates/app.vue @@ -37,10 +37,12 @@ THE SOFTWARE. :options="i18nPacks" :clearable="false" :reduce="s => s.value" + :disabled="languageLoading" > @@ -123,9 +125,11 @@ THE SOFTWARE. import isObject from '../util/is_object'; import analytics from '../util/analytics'; 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 i18nDefault from '../i18n/default'; + import { setLanguagePack } from '../i18n/setup'; import generators from '../generators'; import Domain from './domain'; @@ -155,9 +159,9 @@ THE SOFTWARE. ...Global.delegated, app: { lang: { - default: i18nDefault, - value: i18nDefault, - computed: i18nDefault, + default: defaultPack, + value: defaultPack, + computed: defaultPack, enabled: true, }, }, @@ -168,6 +172,8 @@ THE SOFTWARE. confWatcherWaiting: false, confFilesPrevious: {}, confFilesOutput: {}, + languageLoading: false, + languagePrevious: defaultPack, }; }, computed: { @@ -187,10 +193,10 @@ THE SOFTWARE. }, }, i18nPacks() { - return Object.keys(i18nPacks).map(pack => ({ - label: this.$t(`templates.app.${pack}`) + (pack === this.$i18n.locale + return availablePacks.map(pack => ({ + label: this.$t(`languages.${pack}`) + (pack === this.$i18n.locale ? '' - : ` - ${this.$t(`templates.app.${pack}`, pack)}`), + : ` - ${this.$t(`languages.${pack}`, pack)}`), value: pack, })); }, @@ -208,16 +214,30 @@ THE SOFTWARE. }, '$data.global.app.lang': { handler(data) { + this.$data.languageLoading = true; + // Ensure valid pack - if (!(data.value in i18nPacks)) data.computed = data.default; + if (!availablePacks.includes(data.value)) data.computed = data.default; // Update the locale - this.$i18n.locale = data.computed; - - // Analytics - const pack = data.computed.match(/^([a-z]+)([A-Z]*)$/).slice(1) - .map(x => x.toLowerCase()).filter(x => !!x).join('_'); - analytics(`set_language_${pack}`, 'Language'); + setLanguagePack(data.computed).then(() => { + // Done + console.log('Language set to', data.computed); + this.$data.languagePrevious = data.computed; + this.$data.languageLoading = false; + + // Analytics + analytics(`set_language_${toSep(data.computed, '_')}`, 'Language'); + }).catch((err) => { + // 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, }, @@ -231,7 +251,7 @@ THE SOFTWARE. // Apply browser language if not specified in query if (!imported || !imported.global || !imported.global.app || !imported.global.app.lang) { - const language = browserLanguage(); + const language = browserLanguage(availablePacks); if (language) this.lang = language; } diff --git a/src/nginxconfig/util/browser_language.js b/src/nginxconfig/util/browser_language.js index ec3237c..8581137 100644 --- a/src/nginxconfig/util/browser_language.js +++ b/src/nginxconfig/util/browser_language.js @@ -24,11 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 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 () => { +export default availablePacks => { if (typeof window === 'object' && typeof window.navigator === 'object') { const userLocales = new Set(); @@ -42,12 +40,11 @@ export default () => { userLocales.add(Intl.DateTimeFormat().resolvedOptions().locale); // Try to find an exact region/language match - const i18nPackLocales = Object.keys(i18nPacks); - const exactMatch = [...userLocales.values()].find(locale => i18nPackLocales.includes(toPack(locale))); - if (exactMatch) return toPack(exactMatch); + const exactMatch = [...userLocales.values()].find(locale => availablePacks.includes(fromSep(locale, '-'))); + if (exactMatch) return fromSep(exactMatch, '-'); // 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]; if (!(lang in map)) map[lang] = pack; return map; diff --git a/src/nginxconfig/util/language_pack_context.js b/src/nginxconfig/util/language_pack_context.js new file mode 100644 index 0000000..8e03759 --- /dev/null +++ b/src/nginxconfig/util/language_pack_context.js @@ -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], '-'))); diff --git a/src/nginxconfig/i18n/default.js b/src/nginxconfig/util/language_pack_default.js similarity index 93% rename from src/nginxconfig/i18n/default.js rename to src/nginxconfig/util/language_pack_default.js index adaee9f..aba0bcd 100644 --- a/src/nginxconfig/i18n/default.js +++ b/src/nginxconfig/util/language_pack_default.js @@ -24,4 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -export default 'en'; + +export const defaultPack = 'en'; + +export { default as defaultPackData } from '../i18n/en'; diff --git a/src/nginxconfig/util/language_pack_name.js b/src/nginxconfig/util/language_pack_name.js new file mode 100644 index 0000000..1e485e8 --- /dev/null +++ b/src/nginxconfig/util/language_pack_name.js @@ -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(); diff --git a/test/testBrowserLanguage.js b/test/testBrowserLanguage.js index 8628af2..80ca73f 100644 --- a/test/testBrowserLanguage.js +++ b/test/testBrowserLanguage.js @@ -46,7 +46,6 @@ class MockLocales { } static setDateTimeLocale(locale) { - const newDateTimeFormat = new Intl.DateTimeFormat(locale); MockLocales.IntlBackup = Intl; if (!locale) { // eslint-disable-next-line no-global-assign @@ -54,6 +53,7 @@ class MockLocales { return this; } + const newDateTimeFormat = new Intl.DateTimeFormat(locale); // eslint-disable-next-line no-global-assign Intl = { DateTimeFormat() { @@ -97,13 +97,13 @@ describe('browserLanguage', () => { MockLocales.setDateTimeLocale(undefined); 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']); - expect(browserLanguage()).toEqual('zhTW'); + expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toEqual('zhTW'); MockLocales.setNavigatorLanguages(['zh', 'en-US', 'en']); - expect(browserLanguage()).toEqual('en'); + expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toEqual('en'); MockLocales.restoreDateTimeLocale(); }); @@ -112,7 +112,7 @@ describe('browserLanguage', () => { MockLocales.setDateTimeLocale(undefined); MockLocales.setNavigatorLanguages(['ja-JP', 'ja', 'en-US']); - expect(browserLanguage()).toEqual('en'); + expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toEqual('en'); MockLocales.restoreDateTimeLocale(); }); @@ -121,7 +121,7 @@ describe('browserLanguage', () => { MockLocales.setDateTimeLocale(undefined); MockLocales.setNavigatorLanguages(['ja-JP', 'ja', 'zh']); - expect(browserLanguage()).toEqual('zhCN'); + expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toEqual('zhCN'); MockLocales.restoreDateTimeLocale(); }); @@ -130,7 +130,7 @@ describe('browserLanguage', () => { MockLocales.setDateTimeLocale(undefined); MockLocales.setNavigatorLanguages(['ja-JP','ja']); - expect(browserLanguage()).toBeFalsy(); + expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toBeFalsy(); MockLocales.restoreDateTimeLocale(); }); @@ -140,7 +140,7 @@ describe('browserLanguage', () => { MockLocales.setNavigatorLanguages(undefined); MockLocales.setNavigatorLanguage(undefined); MockLocales.setDateTimeLocale(undefined); - expect(browserLanguage()).toBeFalsy(); + expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toBeFalsy(); MockLocales.restoreDateTimeLocale(); }); @@ -148,7 +148,7 @@ describe('browserLanguage', () => { MockLocales.setNavigatorLanguage('en'); MockLocales.setNavigatorLanguages(undefined); MockLocales.setDateTimeLocale(undefined); - expect(browserLanguage()).toEqual('en'); + expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toEqual('en'); MockLocales.restoreDateTimeLocale(); }); @@ -156,14 +156,14 @@ describe('browserLanguage', () => { MockLocales.setNavigatorLanguage(undefined); MockLocales.setNavigatorLanguages(['en-US','en']); MockLocales.setDateTimeLocale(undefined); - expect(browserLanguage()).toEqual('en'); + expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toEqual('en'); MockLocales.restoreDateTimeLocale(); }); test('navigator is `undefined` and Intl locale is `en-US`',() => { MockLocales.setNavigator(undefined); MockLocales.setDateTimeLocale('en-US'); - expect(browserLanguage()).toEqual('en'); + expect(browserLanguage(['en', 'zhCN', 'zhTW'])).toEqual('en'); MockLocales.restoreDateTimeLocale(); MockLocales.restoreNavigator(); });