mirror of
https://github.com/IT4Change/boilerplate-frontend.git
synced 2025-12-13 07:35:53 +00:00
Merge pull request #7 from IT4Change/lint-locales
feat(frontend): lint locales
This commit is contained in:
commit
5cc5db223c
@ -13,8 +13,7 @@
|
|||||||
"plugin:promise/recommended",
|
"plugin:promise/recommended",
|
||||||
"plugin:security/recommended",
|
"plugin:security/recommended",
|
||||||
"plugin:vue/vue3-recommended",
|
"plugin:vue/vue3-recommended",
|
||||||
"plugin:storybook/recommended",
|
"plugin:storybook/recommended"
|
||||||
"plugin:prettier/recommended"
|
|
||||||
],
|
],
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": "latest",
|
"ecmaVersion": "latest",
|
||||||
@ -23,7 +22,6 @@
|
|||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"@typescript-eslint",
|
"@typescript-eslint",
|
||||||
"prettier",
|
|
||||||
"import",
|
"import",
|
||||||
"promise",
|
"promise",
|
||||||
"security",
|
"security",
|
||||||
@ -40,7 +38,6 @@
|
|||||||
"no-console": "error",
|
"no-console": "error",
|
||||||
"no-debugger": "error",
|
"no-debugger": "error",
|
||||||
"camelcase": "error",
|
"camelcase": "error",
|
||||||
"prettier/prettier": "error",
|
|
||||||
"indent": ["error", 2],
|
"indent": ["error", 2],
|
||||||
"linebreak-style": ["error", "unix"],
|
"linebreak-style": ["error", "unix"],
|
||||||
"semi": ["error", "never"],
|
"semi": ["error", "never"],
|
||||||
@ -120,6 +117,14 @@
|
|||||||
"promise/no-multiple-resolved": "error"
|
"promise/no-multiple-resolved": "error"
|
||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["!*.json"],
|
||||||
|
"plugins": ["prettier"],
|
||||||
|
"extends": ["plugin:prettier/recommended"],
|
||||||
|
"rules": {
|
||||||
|
"prettier/prettier": "error"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"files": ["*.json"],
|
"files": ["*.json"],
|
||||||
"plugins": ["json"],
|
"plugins": ["json"],
|
||||||
@ -131,7 +136,7 @@
|
|||||||
"extends": ["plugin:vuetify/recommended"]
|
"extends": ["plugin:vuetify/recommended"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"files": ["**/*.test.[tj]s"],
|
"files": ["*.[test,spec].[tj]s"],
|
||||||
"plugins": ["vitest"],
|
"plugins": ["vitest"],
|
||||||
"extends": ["plugin:vitest/all"]
|
"extends": ["plugin:vitest/all"]
|
||||||
},
|
},
|
||||||
|
|||||||
55
README.md
55
README.md
@ -2,6 +2,7 @@
|
|||||||
[![nodejs][badge-nodejs-img]][badge-nodejs-href]
|
[![nodejs][badge-nodejs-img]][badge-nodejs-href]
|
||||||
[![npm][badge-npm-img]][badge-npm-href]
|
[![npm][badge-npm-img]][badge-npm-href]
|
||||||
[![docker][badge-docker-img]][badge-docker-href]
|
[![docker][badge-docker-img]][badge-docker-href]
|
||||||
|
[![jq][badge-jq-img]][badge-jq-href]
|
||||||
[![vue][badge-vue-img]][badge-vue-href]
|
[![vue][badge-vue-img]][badge-vue-href]
|
||||||
[![vike][badge-vike-img]][badge-vike-href]
|
[![vike][badge-vike-img]][badge-vike-href]
|
||||||
[![vuetify][badge-vuetify-img]][badge-vuetify-href]
|
[![vuetify][badge-vuetify-img]][badge-vuetify-href]
|
||||||
@ -18,7 +19,7 @@ The IT4C Boilerplate for frontends
|
|||||||
|
|
||||||
## Requirements & Technology
|
## Requirements & Technology
|
||||||
|
|
||||||
To be able to build this project you need `nodejs`, `npm` and optional `docker`.
|
To be able to build this project you need `nodejs`, `npm` and optional `docker` and `jq`.
|
||||||
|
|
||||||
The project uses `vite` as builder, `vike` to do the SSR. The design framework is `vuetify` which requires the frontend framework `vue3`. For localization `vue-i18n` is used; Session storage is handled with `pinia`.
|
The project uses `vite` as builder, `vike` to do the SSR. The design framework is `vuetify` which requires the frontend framework `vue3`. For localization `vue-i18n` is used; Session storage is handled with `pinia`.
|
||||||
|
|
||||||
@ -30,29 +31,30 @@ This projects utilizes `storybook` to develop frontend components and `vuepress`
|
|||||||
|
|
||||||
The following commands are available:
|
The following commands are available:
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
|----------------------------|-------------------------------------------------|
|
|-----------------------------|-------------------------------------------------|
|
||||||
| `npm install` | Project setup |
|
| `npm install` | Project setup |
|
||||||
| `npm run build` | Compiles and minifies for production |
|
| `npm run build` | Compiles and minifies for production |
|
||||||
| `npm run server:prod` | Runs productions server |
|
| `npm run server:prod` | Runs productions server |
|
||||||
| **Develop** | |
|
| **Develop** | |
|
||||||
| `npm run dev` | Compiles and hot-reloads for development |
|
| `npm run dev` | Compiles and hot-reloads for development |
|
||||||
| `npm run server:dev` | Run development server |
|
| `npm run server:dev` | Run development server |
|
||||||
| **Test** | |
|
| **Test** | |
|
||||||
| `npm run test:lint` | Run all linters |
|
| `npm run test:lint` | Run all linters |
|
||||||
| `npm run test:lint:eslint` | Run linter eslint |
|
| `npm run test:lint:eslint` | Run linter eslint |
|
||||||
| `npm run test:lint:remark` | Run linter remark |
|
| `npm run test:lint:locales` | Run linter locales |
|
||||||
| `npm run test:lint:style` | Run linter stylelint |
|
| `npm run test:lint:remark` | Run linter remark |
|
||||||
| `npm run test:unit` | Run all unit tests and generate coverage report |
|
| `npm run test:lint:style` | Run linter stylelint |
|
||||||
| `npm run test:unit:dev` | Run all unit tests in watch mode |
|
| `npm run test:unit` | Run all unit tests and generate coverage report |
|
||||||
| `npm test` | Run all tests & linters |
|
| `npm run test:unit:dev` | Run all unit tests in watch mode |
|
||||||
| **Storybook** | |
|
| `npm test` | Run all tests & linters |
|
||||||
| `npm run storybook` | Run Storybook |
|
| **Storybook** | |
|
||||||
| `npm run storybook:build` | Build static storybook |
|
| `npm run storybook` | Run Storybook |
|
||||||
| `npm run storybook:test` | Run tests against all storybook stories |
|
| `npm run storybook:build` | Build static storybook |
|
||||||
| **Documentation** | |
|
| `npm run storybook:test` | Run tests against all storybook stories |
|
||||||
| `npm run docs:dev` | Run Documentation in development mode |
|
| **Documentation** | |
|
||||||
| `npm run docs:build` | Build static documentation |
|
| `npm run docs:dev` | Run Documentation in development mode |
|
||||||
|
| `npm run docs:build` | Build static documentation |
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
|
|
||||||
@ -81,9 +83,7 @@ The following endpoints are provided given the right command is executed or all
|
|||||||
- [ ] figma
|
- [ ] figma
|
||||||
- [ ] chromatic
|
- [ ] chromatic
|
||||||
- [ ] github actions
|
- [ ] github actions
|
||||||
- [ ] lint locales
|
|
||||||
- [ ] feature zähler -> pinia tore
|
- [ ] feature zähler -> pinia tore
|
||||||
- [ ] lint style (inline vue) stylelint?
|
|
||||||
|
|
||||||
## Known Problems
|
## Known Problems
|
||||||
|
|
||||||
@ -103,6 +103,9 @@ Currently none
|
|||||||
[badge-docker-img]: https://img.shields.io/badge/docker-latest-blue
|
[badge-docker-img]: https://img.shields.io/badge/docker-latest-blue
|
||||||
[badge-docker-href]: https://www.docker.com/
|
[badge-docker-href]: https://www.docker.com/
|
||||||
|
|
||||||
|
[badge-jq-img]: https://img.shields.io/badge/jq-latest-blue
|
||||||
|
[badge-jq-href]: https://jqlang.github.io/jq/
|
||||||
|
|
||||||
[badge-vue-img]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2FIT4Change%2Fboilerplate-frontend%2Fmaster%2Fpackage.json&query=dependencies.vue&label=vue&color=green
|
[badge-vue-img]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2FIT4Change%2Fboilerplate-frontend%2Fmaster%2Fpackage.json&query=dependencies.vue&label=vue&color=green
|
||||||
[badge-vue-href]: https://vuejs.org/
|
[badge-vue-href]: https://vuejs.org/
|
||||||
|
|
||||||
|
|||||||
@ -40,8 +40,9 @@
|
|||||||
"storybook": "storybook dev -p 6006",
|
"storybook": "storybook dev -p 6006",
|
||||||
"storybook:build": "storybook build -o build/storybook",
|
"storybook:build": "storybook build -o build/storybook",
|
||||||
"storybook:test": "test-storybook",
|
"storybook:test": "test-storybook",
|
||||||
"test:lint": "npm run test:lint:eslint && npm run test:lint:remark && npm run test:lint:style",
|
"test:lint": "npm run test:lint:eslint && npm run test:lint:remark && npm run test:lint:style && npm run test:lint:locales",
|
||||||
"test:lint:eslint": "eslint --ext .vue,.ts,.tsx,.js,.jsx,.json,.yml,.yaml --max-warnings 0 --ignore-path .gitignore .",
|
"test:lint:eslint": "eslint --ext .vue,.ts,.tsx,.js,.jsx,.json,.yml,.yaml --max-warnings 0 --ignore-path .gitignore .",
|
||||||
|
"test:lint:locales": "scripts/locales/locales.sh src/locales",
|
||||||
"test:lint:remark": "remark . --quiet --frail",
|
"test:lint:remark": "remark . --quiet --frail",
|
||||||
"test:lint:style": "stylelint --max-warnings 0 --ignore-path .gitignore \"**/*.{css,scss,vue,vuex}\"",
|
"test:lint:style": "stylelint --max-warnings 0 --ignore-path .gitignore \"**/*.{css,scss,vue,vuex}\"",
|
||||||
"test:unit": "npm run test:unit:dev -- run --coverage",
|
"test:unit": "npm run test:unit:dev -- run --coverage",
|
||||||
|
|||||||
@ -2,8 +2,10 @@ import { createI18n } from 'vue-i18n'
|
|||||||
|
|
||||||
// eslint-disable-next-line import/no-relative-parent-imports
|
// eslint-disable-next-line import/no-relative-parent-imports
|
||||||
import de from '../src/locales/de'
|
import de from '../src/locales/de'
|
||||||
|
// import { de as $vuetify } from 'vuetify/locale'
|
||||||
// eslint-disable-next-line import/no-relative-parent-imports
|
// eslint-disable-next-line import/no-relative-parent-imports
|
||||||
import en from '../src/locales/en'
|
import en from '../src/locales/en'
|
||||||
|
// import { en as $vuetify } from 'vuetify/locale'
|
||||||
|
|
||||||
export default createI18n({
|
export default createI18n({
|
||||||
legacy: false, // Vuetify does not support the legacy mode of vue-i18n
|
legacy: false, // Vuetify does not support the legacy mode of vue-i18n
|
||||||
|
|||||||
1
scripts/locales/keys.jq
Normal file
1
scripts/locales/keys.jq
Normal file
@ -0,0 +1 @@
|
|||||||
|
path(..)|[.[]|tostring]|join(".")
|
||||||
44
scripts/locales/locales.sh
Executable file
44
scripts/locales/locales.sh
Executable file
@ -0,0 +1,44 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
if [ $# -eq 0 ]
|
||||||
|
then
|
||||||
|
echo "You have to supply at least one argument specifying the folder to lint"
|
||||||
|
fi
|
||||||
|
|
||||||
|
FILES="$1"
|
||||||
|
|
||||||
|
tmp=$(mktemp)
|
||||||
|
exit_code=0
|
||||||
|
|
||||||
|
for locale_file in $FILES/*.json
|
||||||
|
do
|
||||||
|
jq -f $(dirname "$0")/sort.jq $locale_file > "$tmp"
|
||||||
|
# check sort order and fix it if required
|
||||||
|
if [ "$2" == "--fix" ]
|
||||||
|
then
|
||||||
|
mv "$tmp" $locale_file
|
||||||
|
else
|
||||||
|
if diff -q "$tmp" $locale_file > /dev/null ;
|
||||||
|
then
|
||||||
|
: # all good
|
||||||
|
else
|
||||||
|
exit_code=$?
|
||||||
|
echo "$(basename -- $locale_file) is not sorted by keys"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# check keys
|
||||||
|
if [ -n "$LAST_FILE" ]; then
|
||||||
|
listPaths="jq -f $(dirname "$0")/keys.jq"
|
||||||
|
diffString="<( cat $LAST_FILE | $listPaths ) <( cat $locale_file | $listPaths )"
|
||||||
|
if eval "diff -q $diffString";
|
||||||
|
then
|
||||||
|
: # all good
|
||||||
|
else
|
||||||
|
eval "diff -y $diffString | grep '[|<>]'";
|
||||||
|
printf "\n$LAST_FILE\" and $locale_file translation keys do not match, see diff above.\n"
|
||||||
|
exit_code=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
LAST_FILE=$locale_file
|
||||||
|
done
|
||||||
|
|
||||||
|
exit $exit_code
|
||||||
13
scripts/locales/sort.jq
Normal file
13
scripts/locales/sort.jq
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
def walk(f):
|
||||||
|
. as $in
|
||||||
|
| if type == "object" then
|
||||||
|
reduce keys_unsorted[] as $key
|
||||||
|
( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f
|
||||||
|
elif type == "array" then map( walk(f) ) | f
|
||||||
|
else f
|
||||||
|
end;
|
||||||
|
|
||||||
|
def keys_sort_by(f):
|
||||||
|
to_entries | sort_by(.key|f ) | from_entries;
|
||||||
|
|
||||||
|
walk(if type == "object" then keys_sort_by(ascii_upcase) else . end)
|
||||||
3
src/locales/de.json
Normal file
3
src/locales/de.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"counter": "Zähler"
|
||||||
|
}
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import { de as $vuetify } from 'vuetify/locale'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
counter: 'Zähler',
|
|
||||||
$vuetify,
|
|
||||||
}
|
|
||||||
3
src/locales/en.json
Normal file
3
src/locales/en.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"counter": "Counter"
|
||||||
|
}
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import { en as $vuetify } from 'vuetify/locale'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
counter: 'Counter',
|
|
||||||
$vuetify,
|
|
||||||
}
|
|
||||||
@ -8,7 +8,8 @@
|
|||||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||||
"types": ["vite/client"],
|
"types": ["vite/client"],
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true
|
"esModuleInterop": true,
|
||||||
|
"resolveJsonModule": true
|
||||||
},
|
},
|
||||||
"ts-node": {
|
"ts-node": {
|
||||||
"transpileOnly": true,
|
"transpileOnly": true,
|
||||||
|
|||||||
@ -12,10 +12,10 @@ export default mergeConfig(
|
|||||||
coverage: {
|
coverage: {
|
||||||
all: true,
|
all: true,
|
||||||
include: ['src/**/*.{js,jsx,ts,tsx,vue}'],
|
include: ['src/**/*.{js,jsx,ts,tsx,vue}'],
|
||||||
lines: 5,
|
lines: 2,
|
||||||
functions: 0,
|
functions: 0,
|
||||||
branches: 10,
|
branches: 10,
|
||||||
statements: 5,
|
statements: 2,
|
||||||
// 100: true,
|
// 100: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user