diff --git a/.gitignore b/.gitignore index b1835652..e7217daa 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ /source/fonts/* !/source/fonts/fonts.yaml /source/sass/fonts/ +/source/sass/_locales.scss /vendor/ diff --git a/.stylelintrc b/.stylelintrc index 60812f77..e1e72237 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -2,6 +2,7 @@ "ignoreFiles": [ "./**/!(*.scss)", "./export/css/sass/**/*", + "./source/sass/_locales.scss", "./source/sass/fonts/**/*", "./source/sass/variables/**/*", "./source/sass/vendor/**/*" diff --git a/gulpfile.babel.js b/gulpfile.babel.js index 4d5c571f..a34523e7 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -1,4 +1,5 @@ import browserSync from 'browser-sync'; +import cldr from 'cldr'; import color from 'ansi-colors'; import copy from 'recursive-copy'; import del from 'del'; @@ -29,6 +30,7 @@ import sharp from 'sharp'; import sourcemaps from 'gulp-sourcemaps'; import stylelint from 'stylelint'; import syntaxScss from 'postcss-scss'; +import tags from 'language-tags'; import tempWrite from 'temp-write'; import * as Throttle from 'promise-parallel-throttle'; import url from 'url'; @@ -128,6 +130,7 @@ const buildConfig = (invocationArgs, sourceRoot, testRoot, buildRoot) => { `${config.dir.src.fonts}/**/*`, `!${config.files.src.fontsDefinition}`, ]; + config.files.src.sassLocales = `${config.dir.src.sass}/_locales.scss`; config.files.src.meta = `${config.dir.src.meta}/**/*`; config.files.src.patterns = `${config.dir.src.patterns}/**/*`; config.files.src.templates = `${config.dir.src.patterns}/!(04-pages)/**/*.twig`; @@ -221,6 +224,64 @@ export const buildFonts = gulp.series(cleanFonts, compileFonts); // Sass tasks +const cleanSassLocaleData = () => del([config.files.src.sassLocales]); + +const generateSassLocaleData = done => { + const supplementalMetadata = cldr.extractLanguageSupplementalMetadata(); + const localeParents = cldr.localeIds.filter(locale => locale !== 'root') + .concat(Object.keys(supplementalMetadata).filter(locale => { + return 0 === ['deprecated', 'legacy'].indexOf(supplementalMetadata[locale].reason); + })) + .map(locale => locale.replace(/_/g, '-').toLowerCase()) + .filter(locale => tags.check(locale)) + .reduce((localeParents, locale) => { + const parent = (cldr.resolveParentLocaleId(locale) || 'root').replace(/_/g, '-').toLowerCase(); + + localeParents[locale] = 'root' === parent ? 'und' : parent; + return localeParents; + }, {}); + + const locales = ['und'].concat(Object.keys(localeParents)).reduce((carry, locale) => { + const localeData = {}; + const dataLocale = supplementalMetadata[locale] ? supplementalMetadata[locale]['replacement'] : locale; + + if (locale in localeParents) { + const parent = locale.replace(/-[^-]+$/, ''); + localeData['parent'] = parent === locale ? 'und' : parent; + localeData['real-parent'] = localeParents[locale]; + } + + localeData['inline-list-separator'] = cldr.extractListPatterns(dataLocale).default.middle.replace(/{[0|1]}/g, ''); + localeData['ellipsis-final'] = cldr.extractCharacters(dataLocale).ellipsis.final.replace(/{[0|1]}/g, ''); + localeData['numbering-system'] = cldr.extractDefaultNumberSystemId(dataLocale); + + carry[locale] = localeData; + + return carry; + }, {}); + + const output = fs.createWriteStream(config.files.src.sassLocales); + + output.write('$locale-data: (\n'); + + Object.keys(locales) + .forEach(locale => { + output.write(` "${locale}": (\n`); + Object.keys(locales[locale]).forEach(property => { + output.write(` "${property}": "${locales[locale][property]}",\n`); + }); + output.write(' ),\n'); + }); + + output.write(');\n'); + + output.close(); + + done(); +}; + +export const assembleSassLocaleData = gulp.series(cleanSassLocaleData, generateSassLocaleData); + const lintSass = () => { if (!config.lint) { console.info('Skipping lintSass'); @@ -363,7 +424,7 @@ export const buildPatternLab = gulp.series(cleanPatternLab, generatePatternLab); // Combined tasks -export const build = gulp.series(gulp.parallel(gulp.series(buildFonts, buildCss), buildImages, buildJs), buildPatternLab); +export const build = gulp.series(gulp.parallel(gulp.series(gulp.parallel(assembleSassLocaleData, buildFonts), buildCss), buildImages, buildJs), buildPatternLab); export const test = gulp.parallel(validateJs, validateSass); diff --git a/package-lock.json b/package-lock.json index b5bf94c3..db3ac298 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3587,7 +3587,6 @@ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", "dev": true, - "optional": true, "requires": { "p-finally": "^1.0.0" } @@ -3834,8 +3833,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", @@ -4402,6 +4400,15 @@ "integrity": "sha512-Jt9tIBkRc9POUof7QA/VwWd+58fKkEEfI+/t1/eOlxKM7ZhrczNzMFefge7Ai+39y1pR/pP6cI19guHy3FSLmw==", "dev": true }, + "chainsaw": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.0.9.tgz", + "integrity": "sha1-EaBRAtHEx4W20EFdM21aOhYSkT4=", + "dev": true, + "requires": { + "traverse": ">=0.3.0 <0.4" + } + }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", @@ -4531,6 +4538,24 @@ } } }, + "cldr": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/cldr/-/cldr-4.14.0.tgz", + "integrity": "sha512-M3nhAyMPwInQin4fgeMTngHHZMPkxgiuWoTDteySCsYT38wvTArgX9xVY/vnUIA2degu6sRuouf9Yhmr6+frYQ==", + "dev": true, + "requires": { + "escodegen": "^1.11.0", + "esprima": "^4.0.1", + "lodash": "^4.17.10", + "memoizeasync": "^1.1.0", + "passerror": "^1.1.1", + "pegjs": "^0.10.0", + "seq": "^0.3.5", + "unicoderegexp": "^0.4.1", + "xmldom": "^0.1.27", + "xpath": "^0.0.27" + } + }, "cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", @@ -7739,8 +7764,7 @@ "version": "2.1.1", "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -7764,15 +7788,13 @@ "version": "1.0.0", "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7782,22 +7804,19 @@ "version": "1.1.0", "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -7918,8 +7937,7 @@ "version": "2.0.3", "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -7933,7 +7951,6 @@ "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -7950,7 +7967,6 @@ "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -7959,15 +7975,13 @@ "version": "0.0.8", "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true, - "optional": true + "dev": true }, "mkdirp": { "version": "0.5.1", "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -8056,8 +8070,7 @@ "version": "1.0.1", "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -8071,7 +8084,6 @@ "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -8167,8 +8179,7 @@ "version": "5.1.1", "resolved": false, "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -8210,7 +8221,6 @@ "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -8232,7 +8242,6 @@ "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -8290,8 +8299,7 @@ "version": "1.0.2", "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", @@ -9345,6 +9353,15 @@ "minimalistic-assert": "^1.0.1" } }, + "hashish": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/hashish/-/hashish-0.0.4.tgz", + "integrity": "sha1-bWC8b/r3Ebav1g5CbQd5iAFOZVQ=", + "dev": true, + "requires": { + "traverse": ">=0.2.4" + } + }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", @@ -11449,8 +11466,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -11474,15 +11490,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -11499,22 +11513,19 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -11645,8 +11656,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -11660,7 +11670,6 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -11677,7 +11686,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -11686,15 +11694,13 @@ "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -11715,7 +11721,6 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -11804,8 +11809,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -11819,7 +11823,6 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -11915,8 +11918,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -11958,7 +11960,6 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -11980,7 +11981,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -12029,15 +12029,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "dev": true, - "optional": true + "dev": true } } } @@ -13141,6 +13139,21 @@ "integrity": "sha512-bEZlJzXo5V/ApNNa5z375mJC6Nrz4vG43UgcSCrg2OHC+yuB6j0iDSrY7RQ/+PRofFB03wNIIt9iXIVLr4wc7w==", "dev": true }, + "language-subtag-registry": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.18.tgz", + "integrity": "sha1-gM/oSN+I97+Ajb2tCyjn+iw1eN0=", + "dev": true + }, + "language-tags": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", + "integrity": "sha1-0yHbxNowuovzAk4ED6XBRmH5GTo=", + "dev": true, + "requires": { + "language-subtag-registry": "~0.3.2" + } + }, "last-run": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", @@ -13735,8 +13748,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz", "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==", - "dev": true, - "optional": true + "dev": true }, "mem": { "version": "4.3.0", @@ -13749,6 +13761,24 @@ "p-is-promise": "^2.0.0" } }, + "memoizeasync": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/memoizeasync/-/memoizeasync-1.1.0.tgz", + "integrity": "sha1-nXAopvJm3rczUQu327pfUYeMVh4=", + "dev": true, + "requires": { + "lru-cache": "2.5.0", + "passerror": "1.1.1" + }, + "dependencies": { + "lru-cache": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz", + "integrity": "sha1-2COIrpyWC+y+oMc7uet5tsbOmus=", + "dev": true + } + } + }, "memoizee": { "version": "0.4.14", "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz", @@ -15096,6 +15126,12 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "passerror": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/passerror/-/passerror-1.1.1.tgz", + "integrity": "sha1-oluI292RCilgOux9y5bpp6l2h7Q=", + "dev": true + }, "path-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", @@ -15188,6 +15224,12 @@ "sha.js": "^2.4.8" } }, + "pegjs": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.10.0.tgz", + "integrity": "sha1-z4uvrm7d/0tafvsYUmnqr0YQ3b0=", + "dev": true + }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -16920,6 +16962,16 @@ } } }, + "seq": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/seq/-/seq-0.3.5.tgz", + "integrity": "sha1-rgKvOkJHk9jMvyEtaRdODFTf/jg=", + "dev": true, + "requires": { + "chainsaw": ">=0.0.7 <0.1", + "hashish": ">=0.0.2 <0.1" + } + }, "serialize-javascript": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz", @@ -18538,7 +18590,6 @@ "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-2.0.0.tgz", "integrity": "sha1-awRGhWqbERTRhW/8vlCczLCXcmU=", "dev": true, - "optional": true, "requires": { "temp-dir": "^1.0.0", "uuid": "^3.0.1" @@ -18995,6 +19046,12 @@ } } }, + "traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", + "dev": true + }, "trim": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", @@ -19213,6 +19270,12 @@ "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==", "dev": true }, + "unicoderegexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/unicoderegexp/-/unicoderegexp-0.4.1.tgz", + "integrity": "sha1-r7EOTvHu3ccRQXu7ZSvIhdqdQXE=", + "dev": true + }, "unified": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/unified/-/unified-7.1.0.tgz", @@ -20284,12 +20347,24 @@ "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", "dev": true }, + "xmldom": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", + "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=", + "dev": true + }, "xmlhttprequest-ssl": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", "dev": true }, + "xpath": { + "version": "0.0.27", + "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz", + "integrity": "sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==", + "dev": true + }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", diff --git a/package.json b/package.json index 62ca97b2..fcccc635 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "ansi-colors": "^3.2.4", "babel-loader": "^8.0.5", "browser-sync": "^2.26.5", + "cldr": "^4.13.0", "del": "^3.0.0", "download": "^7.1.0", "eslint": "^5.16.0", @@ -34,6 +35,7 @@ "js-yaml": "^3.13.1", "keyv": "^3.1.0", "keyv-file": "^0.1.13", + "language-tags": "^1.0.5", "minimist": "^1.2.0", "modularscale-sass": "^3.0.8", "normalize.css": "^8.0.1", diff --git a/source/sass/base/_normalize.scss b/source/sass/base/_normalize.scss index ce37c470..f1ad2c62 100644 --- a/source/sass/base/_normalize.scss +++ b/source/sass/base/_normalize.scss @@ -1,3 +1,4 @@ +@import "../functions/locales"; @import "../mixins/decorations"; @import "../mixins/spacing"; @import "../mixins/typography"; @@ -160,72 +161,80 @@ p { } } -:lang(ja) { - @at-root { - em#{&} { - font-style: normal; - text-emphasis-style: open sesame; - text‑emphasis‑position: over right; - } - - strong#{&} { - font-style: normal; - text-emphasis-style: filled sesame; - text‑emphasis‑position: over right; - } - - u#{&} { - text-underline-position: right; +@if is-supported-locale(ja) { + :lang(ja) { + @at-root { + em#{&} { + font-style: normal; + text-emphasis-style: open sesame; + text‑emphasis‑position: over right; + } + + strong#{&} { + font-style: normal; + text-emphasis-style: filled sesame; + text‑emphasis‑position: over right; + } + + u#{&} { + text-underline-position: right; + } } } } -:lang(ko) { - @at-root { - em#{&} { - font-style: normal; - text-decoration: underline; - text-underline-position: under right; - } - - strong#{&} { - font-style: normal; - text-emphasis-style: filled dot; - text‑emphasis‑position: over right; +@if is-supported-locale(ko) { + :lang(ko) { + @at-root { + em#{&} { + font-style: normal; + text-decoration: underline; + text-underline-position: under right; + } + + strong#{&} { + font-style: normal; + text-emphasis-style: filled dot; + text‑emphasis‑position: over right; + } } } } -:lang(zh) { - @at-root { - em#{&} { - font-style: normal; - text-emphasis-style: open dot; - text‑emphasis‑position: under right; - } - - strong#{&} { - font-style: normal; - text-emphasis-style: filled dot; - text‑emphasis‑position: under right; - } - - cite#{&} { - font-style: normal; - text-decoration: underline; - text-underline-style: wavy; +@if is-supported-locale(zh) { + :lang(zh) { + @at-root { + em#{&} { + font-style: normal; + text-emphasis-style: open dot; + text‑emphasis‑position: under right; + } + + strong#{&} { + font-style: normal; + text-emphasis-style: filled dot; + text‑emphasis‑position: under right; + } + + cite#{&} { + font-style: normal; + text-decoration: underline; + text-underline-style: wavy; + } } } } -:lang(zh-Hant) { - @at-root { - em#{&} { - text‑emphasis‑position: over right; - } +@if is-supported-locale(zh-Hant) { + :lang(zh-Hant) { + @at-root { + em#{&} { + text‑emphasis‑position: over right; + } - strong#{&} { - text‑emphasis‑position: over right; + strong#{&} { + text‑emphasis‑position: over right; + } } } } diff --git a/source/sass/functions/_locales.scss b/source/sass/functions/_locales.scss new file mode 100644 index 00000000..8a0c3bab --- /dev/null +++ b/source/sass/functions/_locales.scss @@ -0,0 +1,44 @@ +@import "utilities"; +@import "../locales"; +@import "../settings/locales"; + +@function is-supported-locale($locale) { + $locale: to-lower-case($locale); + + @if "und" == $locale { + @return true; + } + + @each $supportedLocale in $locales { + $supportedLocale: to-lower-case($supportedLocale); + + @if $supportedLocale == $locale or string-starts-with($locale, "#{$supportedLocale}-") { + @return true; + } + } + + @return false; +} + +@function get-locale-data($property) { + $result: (); + + @each $locale, $data in $locale-data { + @if is-supported-locale($locale) and map-has-key($data, $property) { + $value: map-get($data, $property); + + @if "parent" != $property and map-has-key($data, "parent") { + $parentLocale: map-get($data, "parent"); + $parentData: map-get($locale-data, $parentLocale); + + @if map-get($parentData, $property) != $value or not is-supported-locale($parentLocale) { + $result: map-merge($result, ($locale: character-to-keyword($value))); + } + } @else { + $result: map-merge($result, ($locale: character-to-keyword($value))); + } + } + } + + @return $result; +} diff --git a/source/sass/functions/_utilities.scss b/source/sass/functions/_utilities.scss new file mode 100644 index 00000000..60955ff1 --- /dev/null +++ b/source/sass/functions/_utilities.scss @@ -0,0 +1,54 @@ +@function string-starts-with($string, $test) { + @return str-index($string, $test) == 1; +} + +@function str-last-index($string, $substr) { + $index: null; + $length: str-length($string); + @for $n from $length through 1 { + $index: str-index(str-slice($string, $n, $length), $substr); + @if $index { + @return $index + $n - 1; + } + } + @return $index; +} + +@function character-to-keyword($character) { + $map: ( + "…": ellipsis, + ); + + @if index($map, $character) { + @return map-get($map, $character); + } + + @return $character; +} + +@function numbering-system-to-keyword($system) { + $unsupported: ( + "cakm", + ); + + $map: ( + "arab": arabic-indic, + "arabext": persian, + "armn": armenian, + "beng": bengali, + "deva": devanagari, + "latn": decimal, + "mymr": myanmar, + "tibt": tibetan, + ); + + @if map-has-key($map, $system) { + @return map-get($map, $system); + } + + @if index($unsupported, $system) { + @return map-get($map, "latn"); + } + + @error "Unknown numbering system '#{$system}'"; +} diff --git a/source/sass/mixins/_lists.scss b/source/sass/mixins/_lists.scss new file mode 100644 index 00000000..9a098a16 --- /dev/null +++ b/source/sass/mixins/_lists.scss @@ -0,0 +1,56 @@ +@import "spacing"; +@import "../functions/locales"; + +@mixin list-style-none() { + @include padding(0, inline-start); + // Overflow auto is so the bullet will be still read from a screen reader and will push the bullet off screen + overflow-x: auto; +} + +@mixin list-separator($separator: default) { + + @if $separator == default { + + @each $locale, $separator in get-locale-data(inline-list-separator) { + + @if $locale == "und" { + + &:after { + content: $separator; + } + + } @else { + + &:lang(#{$locale}):after { + content: $separator; + } + + } + + } + + &:last-child:after { + content: ""; + } + + } @else if $separator == alternative { + + &:after { + content: ";\a0"; + } + + @if is-supported-locale(ar) { + &:lang(ar):after { + content: "\61b\a0"; + } + } + + &:last-child:after { + content: ""; + } + + } @else { + @include _error("Unknown list separator '#{$separator}'"); + } + +} diff --git a/source/sass/mixins/_sizes.scss b/source/sass/mixins/_sizes.scss index eeff4d39..699e287c 100644 --- a/source/sass/mixins/_sizes.scss +++ b/source/sass/mixins/_sizes.scss @@ -1,6 +1,7 @@ @import "logical-properties"; @import "types"; @import "validation"; +@import "../functions/locales"; @mixin block-size($size) { @include _expect_single_value($size) { @@ -48,10 +49,21 @@ @mixin truncate-with-ellipsis() { overflow: hidden; - text-overflow: ellipsis; white-space: nowrap; - &:lang(zh-Hant-HK) { - text-overflow: "⋯"; + @each $locale, $ellipsis in get-locale-data(ellipsis-final) { + + @if $locale == "und" { + + text-overflow: $ellipsis; + + } @else { + + &:lang(#{$locale}) { + text-overflow: $ellipsis; + } + + } + } } diff --git a/source/sass/mixins/_typography.scss b/source/sass/mixins/_typography.scss index 6369aa96..699500e1 100644 --- a/source/sass/mixins/_typography.scss +++ b/source/sass/mixins/_typography.scss @@ -128,49 +128,3 @@ font-family: $font-secondary; @include set-font-size-and-line-height(scale(-2)); } - -@mixin list-style-none() { - @include padding(0, inline-start); - // Overflow auto is so the bullet will be still read from a screen reader and will push the bullet off screen - overflow-x: auto; -} - -@mixin list-separator($separator: default) { - - @if $separator == default { - - &:after { - content: ",\a0"; - } - - &:lang(ar):after { - content: "،\a0"; - } - - &:lang(ja):after { - content: "、"; - } - - &:last-child:after { - content: ""; - } - - } @else if $separator == alternative { - - &:after { - content: ";\a0"; - } - - &:lang(ar):after { - content: "\61b\a0"; - } - - &:last-child:after { - content: ""; - } - - } @else { - @include _error("Unknown list separator '#{$separator}'"); - } - -} diff --git a/source/sass/patterns/molecules/content-meta.scss b/source/sass/patterns/molecules/content-meta.scss index 940328da..142b677a 100644 --- a/source/sass/patterns/molecules/content-meta.scss +++ b/source/sass/patterns/molecules/content-meta.scss @@ -1,3 +1,4 @@ +@import "../../mixins/lists"; @import "../../mixins/spacing"; @import "../../mixins/typography"; diff --git a/source/sass/patterns/molecules/inline-list.scss b/source/sass/patterns/molecules/inline-list.scss index c0fd6a35..301c5437 100644 --- a/source/sass/patterns/molecules/inline-list.scss +++ b/source/sass/patterns/molecules/inline-list.scss @@ -1,3 +1,4 @@ +@import "../../mixins/lists"; @import "../../mixins/typography"; .inline-list { diff --git a/source/sass/patterns/molecules/nav-bar.scss b/source/sass/patterns/molecules/nav-bar.scss index 7b3f434c..38a8c31f 100644 --- a/source/sass/patterns/molecules/nav-bar.scss +++ b/source/sass/patterns/molecules/nav-bar.scss @@ -1,3 +1,4 @@ +@import "../../mixins/lists"; @import "../../mixins/media-query"; @import "../../mixins/scale"; @import "../../mixins/spacing"; diff --git a/source/sass/patterns/molecules/tag-list.scss b/source/sass/patterns/molecules/tag-list.scss index 1274671c..1b7ecdd9 100644 --- a/source/sass/patterns/molecules/tag-list.scss +++ b/source/sass/patterns/molecules/tag-list.scss @@ -1,3 +1,4 @@ +@import "../../mixins/lists"; @import "../../mixins/sizes"; @import "../../mixins/spacing"; @import "../../mixins/typography"; diff --git a/source/sass/patterns/organisms/reference-list.scss b/source/sass/patterns/organisms/reference-list.scss index a7db9751..1c768131 100644 --- a/source/sass/patterns/organisms/reference-list.scss +++ b/source/sass/patterns/organisms/reference-list.scss @@ -1,3 +1,5 @@ +@import "../../functions/locales"; +@import "../../mixins/lists"; @import "../../mixins/positioning"; @import "../../mixins/spacing"; @import "../../mixins/typography"; @@ -16,24 +18,27 @@ $_ordinal-inline-size--large: 3rem; --ordinal-inline-size: #{$_ordinal-inline-size}; - @supports (content: counter(reference-count, cjk-decimal)) { - &:lang(ja) { - --ordinal-inline-size: #{$_ordinal-inline-size--large}; + @each $locale, $numbering-system in get-locale-data(numbering-system) { + + $numbering-system: numbering-system-to-keyword($numbering-system); + + @if $locale == "und" { &:before { - content: counter(reference-count, cjk-decimal); + content: counter(reference-count, $numbering-system); + } + + } @else { + + &:lang(#{$locale}):before { + content: counter(reference-count, $numbering-system); } - } - } - &:lang(ar) { - &:before { - content: counter(reference-count, arabic-indic); } + } &:before { - content: counter(reference-count, decimal); font-weight: 600; font-family: $font-secondary; display: inline-block; diff --git a/source/sass/patterns/organisms/teaser-list.scss b/source/sass/patterns/organisms/teaser-list.scss index a592442a..86b097eb 100644 --- a/source/sass/patterns/organisms/teaser-list.scss +++ b/source/sass/patterns/organisms/teaser-list.scss @@ -1,4 +1,5 @@ @import "../../mixins/decorations"; +@import "../../mixins/lists"; @import "../../mixins/spacing"; @import "../../mixins/typography"; @import "../../settings/color"; diff --git a/source/sass/settings/_locales.scss b/source/sass/settings/_locales.scss new file mode 100644 index 00000000..1821d4f9 --- /dev/null +++ b/source/sass/settings/_locales.scss @@ -0,0 +1,6 @@ +$locales: ( + "ar", + "en", + "ja", + "ru", +) !default; diff --git a/test/sass/mixins/lists.spec.scss b/test/sass/mixins/lists.spec.scss new file mode 100644 index 00000000..3a609473 --- /dev/null +++ b/test/sass/mixins/lists.spec.scss @@ -0,0 +1,74 @@ +@import "../test"; +@import "../../../source/sass/mixins/lists"; + +@include describe("@mixin list-separator") { + + @include it("Provides the default separator correctly") { + + @include assert() { + + @include output { + @include list-separator(default); + } + + @include contains { + &:last-child:after { + content: ""; + } + } + } + + @include assert() { + + @include output { + @include list-separator(); + } + + @include contains { + &:last-child:after { + content: ""; + } + } + } + + } + + @include it("Provides the alternative separator correctly") { + + @include assert() { + + @include output { + @include list-separator(alternative); + } + + @include expect { + &:after { + content: ";\a0"; + } + + &:lang(ar):after { + content: "\61b\a0"; + } + + &:last-child:after { + content: ""; + } + } + } + + } + + @include it("errors on invalid separators") { + @include assert() { + + @include output { + @include list-separator("invalid"); + } + + @include expect { + _error: "Unknown list separator 'invalid'"; + } + } + }; + +}; diff --git a/test/sass/mixins/typography.spec.scss b/test/sass/mixins/typography.spec.scss index f8ba5208..62fb8480 100644 --- a/test/sass/mixins/typography.spec.scss +++ b/test/sass/mixins/typography.spec.scss @@ -87,99 +87,3 @@ } } - -@include describe("@mixin list-separator") { - - @include it("Provides the default separator correctly") { - - @include assert() { - - @include output { - @include list-separator(default); - } - - @include expect { - &:after { - content: ",\a0"; - } - - &:lang(ar):after { - content: "،\a0"; - } - - &:lang(ja):after { - content: "、"; - } - - &:last-child:after { - content: ""; - } - } - } - - @include assert() { - - @include output { - @include list-separator(); - } - - @include expect { - &:after { - content: ",\a0"; - } - - &:lang(ar):after { - content: "،\a0"; - } - - &:lang(ja):after { - content: "、"; - } - - &:last-child:after { - content: ""; - } - } - } - - } - - @include it("Provides the alternative separator correctly") { - - @include assert() { - - @include output { - @include list-separator(alternative); - } - - @include expect { - &:after { - content: ";\a0"; - } - - &:lang(ar):after { - content: "\61b\a0"; - } - - &:last-child:after { - content: ""; - } - } - } - - } - - @include it("errors on invalid separators") { - @include assert() { - - @include output { - @include list-separator("invalid"); - } - - @include expect { - _error: "Unknown list separator 'invalid'"; - } - } - }; - -};