Compare commits
15 Commits
v2.0.0-bet
...
v2.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fac0479b25 | ||
|
|
b2edda410f | ||
|
|
73465268e2 | ||
|
|
4e449235b2 | ||
|
|
1a7f969b59 | ||
|
|
e7e6ea7dda | ||
|
|
9f522dd727 | ||
|
|
76e8a56cc7 | ||
|
|
012eb74837 | ||
|
|
207a63c0d8 | ||
|
|
bbc381e70c | ||
|
|
de45906860 | ||
|
|
1734ef7c74 | ||
|
|
c4d368eee9 | ||
|
|
f0ff18c792 |
14
CHANGELOG.md
@@ -1,4 +1,16 @@
|
||||
# 2.0.0-beta.1 (2019-11-02)
|
||||
# 2.0.0-beta.3 (2019-12-13)
|
||||
|
||||
- Add i18 support (currently English and German, `locale` config option)
|
||||
- Add custom checkbox focus style
|
||||
- Fix layer dropdown issues
|
||||
- Fix checkbox style issues
|
||||
- Fix hover/focus inconsistencies
|
||||
- Fix Docker image labels
|
||||
- `README.md` enhancements
|
||||
- Upgrade dependencies
|
||||
|
||||
|
||||
# 2.0.0-beta.2 (2019-11-02)
|
||||
|
||||
- Add `onLocationChange.reloadHistory` config option
|
||||
- Add Travis CI config
|
||||
|
||||
97
README.md
@@ -1,14 +1,11 @@
|
||||
# OwnTracks UI
|
||||
|
||||
> Web interface for OwnTracks
|
||||
|
||||

|
||||
[](https://hub.docker.com/r/owntracks/frontend)
|
||||
[](https://travis-ci.org/owntracks/frontend)
|
||||
[](https://github.com/owntracks/frontend/blob/master/LICENSE)
|
||||
|
||||
<p style="text-align: center;">
|
||||
<img src="https://raw.githubusercontent.com/owntracks/frontend/master/docs/images/owntracks-ui.png" alt="OwnTracks UI">
|
||||
</p>
|
||||

|
||||
|
||||
## Introduction
|
||||
|
||||
@@ -16,22 +13,33 @@ This is a web interface for [OwnTracks](https://github.com/owntracks/recorder) b
|
||||
a Vue.js single page application. The recorder itself already ships with some basic web
|
||||
pages, this is a more advanced interface with more functionality, all in one place.
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
- Last known (i.e. live) locations:
|
||||
- Accuracy visualization (circle)
|
||||
- Device friendly name and icon
|
||||
- Detailed information (if available): time, latitude, longitude, height, battery and
|
||||
speed
|
||||
- Location history (data points, line or both)
|
||||
- Location heatmap
|
||||
- Quickly fit all shown objects on the map into view
|
||||
- Display data in a specific date range
|
||||
- Filter by user and device
|
||||
- Download selected location data as JSON
|
||||
- Highly customisable
|
||||
|
||||
## Installation
|
||||
|
||||
### Manually
|
||||
|
||||
- Run `yarn install --production` to install dependencies
|
||||
- Run `yarn build` to compile and minify for production
|
||||
- Copy the content of the `dist/` directory to your webroot
|
||||
|
||||
The API is expected to be reachable under the same domain as the web interface.
|
||||
|
||||
### Docker
|
||||
|
||||
You can launch directly via Docker run like this:
|
||||
A pre-built Docker image is available on Docker Hub as [`owntracks/frontend`](https://hub.docker.com/r/owntracks/frontend).
|
||||
|
||||
You can start a container directly via `docker run`:
|
||||
|
||||
```console
|
||||
$ docker run -d -p 80:80 -e SERVER_HOST=otrecorder-host -e SERVER_PORT=otrecorder-port owntracks/frontend
|
||||
$ docker run -d -p 80:80 -e SERVER_HOST=otrecorder-host -e SERVER_PORT=8083 owntracks/frontend
|
||||
```
|
||||
|
||||
Or you can use `docker-compose` (if you also run the OwnTracks Recorder with the default
|
||||
@@ -53,6 +61,26 @@ services:
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
To change the port on which the nginx server will listen on, set the
|
||||
`LISTEN_PORT` enviroment variable - default is 80.
|
||||
|
||||
To build the image from source replace `image:` with:
|
||||
|
||||
```yaml
|
||||
build:
|
||||
context: ./owntracks-frontend
|
||||
dockerfile: docker/Dockerfile
|
||||
```
|
||||
|
||||
(assuming you have this repository cloned to `owntracks-frontend` in the same
|
||||
directory as `docker-compose.yml`)
|
||||
|
||||
### Manually
|
||||
|
||||
- Run `yarn install --production` to install dependencies
|
||||
- Run `yarn build` to compile and minify for production
|
||||
- Copy the content of the `dist/` directory to your webroot
|
||||
|
||||
## Configuration
|
||||
|
||||
It's possible to get started without any configuration change whatsoever, assuming your
|
||||
@@ -70,6 +98,8 @@ See [`docs/config.md`](docs/config.md) for all available options.
|
||||
- Run `yarn lint` to lint and fix files
|
||||
- Run `yarn test` to run unit tests
|
||||
|
||||
### CORS-Proxy
|
||||
|
||||
You can use the [`corsProxy.js`](scripts/corsProxy.js) script to use your production
|
||||
instance of OwnTracks for development without making changes to its CORS-Headers:
|
||||
|
||||
@@ -95,29 +125,30 @@ and `OT_PROXY_PORT` environment variables.
|
||||
|
||||
Finally update `api.baseUrl` in your config to `"http://0.0.0.0:8888/https://owntracks.example.com"`.
|
||||
|
||||
## Features
|
||||
### I18n
|
||||
|
||||
- Last known (i.e. live) locations:
|
||||
- Accuracy visualization (circle)
|
||||
- Device friendly name and icon
|
||||
- Detailed information (if available): time, latitude, longitude, height, battery and
|
||||
speed
|
||||
- Location history (data points, line or both)
|
||||
- Location heatmap
|
||||
- Quickly fit all shown objects on the map into view
|
||||
- Display data in a specific date range
|
||||
- Filter by user and device
|
||||
- Highly customisable
|
||||
This project uses [Vue I18n](https://kazupon.github.io/vue-i18n/). To see missing and
|
||||
unused i18n entries, run:
|
||||
|
||||
```console
|
||||
$ yarn i18n:report
|
||||
```
|
||||
|
||||
To add a new locale, copy `en.json` to `<locale>.json` in [`src/locales`](src/locales)
|
||||
and start translating the individual strings. Make sure to add the new locale to the docs!
|
||||
|
||||
## Screenshots
|
||||
|
||||
_Click to enlarge._
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/owntracks/frontend/master/docs/images/loading.gif" alt="Loading...">
|
||||
<br>
|
||||
<br>
|
||||
<img src="https://raw.githubusercontent.com/owntracks/frontend/master/docs/images/downloader.png" alt="Download location data">
|
||||
<br>
|
||||
<br>
|
||||
<img src="https://raw.githubusercontent.com/owntracks/frontend/master/docs/images/info.png" alt="Info">
|
||||
</p>
|
||||
|
||||
<a href="https://raw.githubusercontent.com/owntracks/frontend/master/docs/images/live.png" target="_blank"><img src="https://raw.githubusercontent.com/owntracks/frontend/master/docs/images/live.png" alt="Live" height="200"></a>
|
||||
<a href="https://raw.githubusercontent.com/owntracks/frontend/master/docs/images/multiple.png" target="_blank"><img src="https://raw.githubusercontent.com/owntracks/frontend/master/docs/images/multiple.png" alt="Multiple" height="200"></a>
|
||||
<a href="https://raw.githubusercontent.com/owntracks/frontend/master/docs/images/date-selection.png" target="_blank"><img src="https://raw.githubusercontent.com/owntracks/frontend/master/docs/images/date-selection.png" alt="Date selection" height="200"></a>
|
||||
<a href="https://raw.githubusercontent.com/owntracks/frontend/master/docs/images/heatmap.png" target="_blank"><img src="https://raw.githubusercontent.com/owntracks/frontend/master/docs/images/heatmap.png" alt="Heatmap" height="200"></a>
|
||||
<a href="https://raw.githubusercontent.com/owntracks/frontend/master/docs/images/customized.png" target="_blank"><img src="https://raw.githubusercontent.com/owntracks/frontend/master/docs/images/customized.png" alt="Customized" height="200"></a>
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
FROM node:10 as build
|
||||
LABEL version="2.0.0-beta.2"
|
||||
LABEL description="OwnTracks UI"
|
||||
LABEL maintainer="Linus Groh <mail@linusgroh.de>"
|
||||
WORKDIR /usr/src/app
|
||||
COPY package.json yarn.lock ./
|
||||
RUN yarn install
|
||||
@@ -9,6 +6,9 @@ COPY . ./
|
||||
RUN yarn build
|
||||
|
||||
FROM nginx:1.17-alpine
|
||||
LABEL version="2.0.0-beta.3"
|
||||
LABEL description="OwnTracks UI"
|
||||
LABEL maintainer="Linus Groh <mail@linusgroh.de>"
|
||||
ENV LISTEN_PORT=80 \
|
||||
SERVER_HOST=otrecorder \
|
||||
SERVER_PORT=80
|
||||
|
||||
@@ -26,6 +26,7 @@ window.owntracks.config = {};
|
||||
- [`baseUrl`](#apibaseurl)
|
||||
- [`endDate`](#enddate)
|
||||
- [`ignorePingLocation`](#ignorepinglocation)
|
||||
- [`locale`](#locale)
|
||||
- `map`
|
||||
- [`attribution`](#mapattribution)
|
||||
- `center`
|
||||
@@ -120,6 +121,13 @@ Remove the `ping/ping` location from the fetched data. This is useful when using
|
||||
};
|
||||
```
|
||||
|
||||
### `locale`
|
||||
|
||||
The language to use for the user interface. Available: `de` (German), `en` (English).
|
||||
|
||||
- Type: [`String`]
|
||||
- Default: `"en"`
|
||||
|
||||
### `map.attribution`
|
||||
|
||||
Attribution for map tiles.
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 44 KiB |
BIN
docs/images/downloader.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 4.4 MiB |
BIN
docs/images/info.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 63 KiB |
BIN
docs/images/loading.gif
Normal file
|
After Width: | Height: | Size: 370 KiB |
BIN
docs/images/map-features.png
Normal file
|
After Width: | Height: | Size: 752 KiB |
|
Before Width: | Height: | Size: 411 KiB |
|
Before Width: | Height: | Size: 540 KiB After Width: | Height: | Size: 452 KiB |
30
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "owntracks-ui",
|
||||
"version": "2.0.0-beta.2",
|
||||
"version": "2.0.0-beta.3",
|
||||
"author": {
|
||||
"name": "Linus Groh",
|
||||
"email": "mail@linusgroh.de"
|
||||
@@ -10,40 +10,44 @@
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint",
|
||||
"cors-proxy": "node scripts/corsProxy.js",
|
||||
"i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'",
|
||||
"test": "vue-cli-service test:unit"
|
||||
},
|
||||
"dependencies": {
|
||||
"clipboard-copy": "^3.1.0",
|
||||
"core-js": "^3.3.6",
|
||||
"core-js": "^3.4.8",
|
||||
"deepmerge": "^4.2.2",
|
||||
"leaflet": "^1.5.1",
|
||||
"leaflet": "^1.6.0",
|
||||
"leaflet.heat": "^0.2.0",
|
||||
"vue": "^2.6.6",
|
||||
"vue-feather-icons": "^5.0.0",
|
||||
"vue-i18n": "^8.0.0",
|
||||
"vue-js-modal": "^1.3.31",
|
||||
"vue-outside-events": "^1.1.3",
|
||||
"vue-router": "^3.1.3",
|
||||
"vue2-leaflet": "^2.2.1",
|
||||
"vuejs-datepicker": "^1.6.2",
|
||||
"vuex": "^3.1.1"
|
||||
"vuex": "^3.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^4.0.5",
|
||||
"@vue/cli-plugin-eslint": "^4.0.5",
|
||||
"@vue/cli-plugin-unit-jest": "^4.0.5",
|
||||
"@vue/cli-service": "^4.0.5",
|
||||
"@vue/eslint-config-prettier": "^5.0.0",
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"@vue/cli-plugin-babel": "^4.1.1",
|
||||
"@vue/cli-plugin-eslint": "^4.1.1",
|
||||
"@vue/cli-plugin-unit-jest": "^4.1.1",
|
||||
"@vue/cli-service": "^4.1.1",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"@vue/test-utils": "1.0.0-beta.30",
|
||||
"babel-core": "7.0.0-bridge.0",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"babel-jest": "^24.9.0",
|
||||
"cors-anywhere": "^0.4.1",
|
||||
"eslint": "^6.6.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-prettier": "^3.1.1",
|
||||
"eslint-plugin-vue": "^5.2.3",
|
||||
"eslint-plugin-vue": "^6.0.1",
|
||||
"jest-fetch-mock": "^2.1.2",
|
||||
"lint-staged": "^9.4.2",
|
||||
"lint-staged": "^9.5.0",
|
||||
"node-sass": "^4.13.0",
|
||||
"sass-loader": "^8.0.0",
|
||||
"vue-cli-plugin-i18n": "^0.6.0",
|
||||
"vue-template-compiler": "^2.5.21"
|
||||
},
|
||||
"license": "MIT",
|
||||
|
||||
@@ -4,73 +4,31 @@
|
||||
<div class="nav-item">
|
||||
<button
|
||||
class="button button-outline"
|
||||
title="Automatically center the map view and zoom in to relevant data"
|
||||
:title="
|
||||
$t('Automatically center the map view and zoom in to relevant data')
|
||||
"
|
||||
@click="$root.$emit('fitView')"
|
||||
>
|
||||
Fit View
|
||||
{{ $t("Fit view") }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<LayersIcon size="1x" />
|
||||
<div class="dropdown">
|
||||
<button class="dropdown-button button" title="Show/hide layers">
|
||||
Layer Settings
|
||||
</button>
|
||||
<div class="dropdown-body">
|
||||
<label tabindex="0">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="map.layers.last"
|
||||
@change="
|
||||
setMapLayerVisibility({
|
||||
layer: 'last',
|
||||
visibility: $event.target.checked,
|
||||
})
|
||||
"
|
||||
/>
|
||||
Show last known locations
|
||||
</label>
|
||||
<label tabindex="0">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="map.layers.line"
|
||||
@change="
|
||||
setMapLayerVisibility({
|
||||
layer: 'line',
|
||||
visibility: $event.target.checked,
|
||||
})
|
||||
"
|
||||
/>
|
||||
Show location history (line)
|
||||
</label>
|
||||
<label tabindex="0">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="map.layers.points"
|
||||
@change="
|
||||
setMapLayerVisibility({
|
||||
layer: 'points',
|
||||
visibility: $event.target.checked,
|
||||
})
|
||||
"
|
||||
/>
|
||||
Show location history (points)
|
||||
</label>
|
||||
<label tabindex="0">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="map.layers.heatmap"
|
||||
@change="
|
||||
setMapLayerVisibility({
|
||||
layer: 'heatmap',
|
||||
visibility: $event.target.checked,
|
||||
})
|
||||
"
|
||||
/>
|
||||
Show location heatmap
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<Dropdown :label="$t('Layer settings')" :title="$t('Show/hide layers')">
|
||||
<label v-for="option in layerSettingsOptions" :key="option.layer">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="map.layers[option.layer]"
|
||||
@change="
|
||||
setMapLayerVisibility({
|
||||
layer: option.layer,
|
||||
visibility: $event.target.checked,
|
||||
})
|
||||
"
|
||||
/>
|
||||
{{ option.label }}
|
||||
</label>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<CalendarIcon size="1x" />
|
||||
@@ -78,14 +36,14 @@
|
||||
v-model="startDate"
|
||||
:use-utc="true"
|
||||
:disabled-dates="startDateDisabledDates"
|
||||
title="Select start date"
|
||||
:title="$t('Select start date')"
|
||||
/>
|
||||
to
|
||||
<Datepicker
|
||||
v-model="endDate"
|
||||
:use-utc="true"
|
||||
:disabled-dates="endDateDisabledDates"
|
||||
title="Select end date"
|
||||
:title="$t('Select end date')"
|
||||
/>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
@@ -93,10 +51,10 @@
|
||||
<select
|
||||
v-model="selectedUser"
|
||||
class="dropdown-button button"
|
||||
title="Select user"
|
||||
:title="$t('Select user')"
|
||||
>
|
||||
<option :value="null">
|
||||
Show All
|
||||
{{ $t("Show all") }}
|
||||
</option>
|
||||
<option v-for="user in users" :value="user" :key="user">
|
||||
{{ user }}
|
||||
@@ -108,10 +66,10 @@
|
||||
<select
|
||||
v-model="selectedDevice"
|
||||
class="dropdown-button button"
|
||||
title="Select device"
|
||||
:title="$t('Select device')"
|
||||
>
|
||||
<option :value="null">
|
||||
Show All
|
||||
{{ $t("Show all") }}
|
||||
</option>
|
||||
<option
|
||||
v-for="device in devices[selectedUser]"
|
||||
@@ -127,7 +85,7 @@
|
||||
<div class="nav-item">
|
||||
<button
|
||||
class="button button-flat button-icon"
|
||||
title="Download raw data"
|
||||
:title="$t('Download raw data')"
|
||||
@click="$modal.show('download')"
|
||||
>
|
||||
<DownloadIcon size="1x" />
|
||||
@@ -136,7 +94,7 @@
|
||||
<div class="nav-item">
|
||||
<button
|
||||
class="button button-flat button-icon"
|
||||
title="Information"
|
||||
:title="$t('Information')"
|
||||
@click="$modal.show('information')"
|
||||
>
|
||||
<InfoIcon size="1x" />
|
||||
@@ -158,6 +116,7 @@ import {
|
||||
} from "vue-feather-icons";
|
||||
import Datepicker from "vuejs-datepicker";
|
||||
|
||||
import Dropdown from "@/components/Dropdown";
|
||||
import * as types from "@/store/mutation-types";
|
||||
|
||||
export default {
|
||||
@@ -169,6 +128,17 @@ export default {
|
||||
SmartphoneIcon,
|
||||
UserIcon,
|
||||
Datepicker,
|
||||
Dropdown,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
layerSettingsOptions: [
|
||||
{ layer: "last", label: this.$t("Show last known locations") },
|
||||
{ layer: "line", label: this.$t("Show location history (line)") },
|
||||
{ layer: "points", label: this.$t("Show location history (points)") },
|
||||
{ layer: "heatmap", label: this.$t("Show location heatmap") },
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["users", "devices", "map"]),
|
||||
|
||||
38
src/components/Dropdown.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div class="dropdown" v-focus-outside="hide" v-click-outside="hide">
|
||||
<button class="dropdown-button button" :title="title" @click="toggle">
|
||||
{{ label }}
|
||||
</button>
|
||||
<div v-if="active" class="dropdown-body">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
active: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
this.active = !this.active;
|
||||
},
|
||||
hide() {
|
||||
this.active = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -8,15 +8,15 @@
|
||||
id="option-minify-json"
|
||||
/>
|
||||
<label for="option-minify-json">
|
||||
Minify JSON
|
||||
{{ $t("Minify JSON") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button class="button button-outline button-primary" @click="copy">
|
||||
Copy to Clipboard
|
||||
{{ $t("Copy to clipboard") }}
|
||||
</button>
|
||||
<button class="button button-primary" @click="download">
|
||||
Download
|
||||
{{ $t("Download") }}
|
||||
</button>
|
||||
</div>
|
||||
</modal>
|
||||
|
||||
@@ -18,19 +18,19 @@
|
||||
<li>
|
||||
<GlobeIcon size="1x" />
|
||||
<a href="https://owntracks.org">
|
||||
OwnTracks Website
|
||||
{{ $t("OwnTracks website") }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<BookIcon size="1x" />
|
||||
<a href="https://owntracks.org/booklet/">
|
||||
OwnTracks Documentation
|
||||
{{ $t("OwnTracks documentation") }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<TwitterIcon size="1x" />
|
||||
<a href="https://twitter.com/OwnTracks">
|
||||
OwnTracks on Twitter
|
||||
{{ $t("OwnTracks on Twitter") }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<modal name="loading" :click-to-close="false" adaptive>
|
||||
<LoaderIcon class="loader" size="1.5x" />
|
||||
Loading data, please wait...
|
||||
{{ $t("Loading data, please wait...") }}
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ const DEFAULT_CONFIG = {
|
||||
},
|
||||
endDate,
|
||||
ignorePingLocation: true,
|
||||
locale: "en",
|
||||
map: {
|
||||
attribution:
|
||||
'© <a href="https://osm.org/copyright">OpenStreetMap</a> contributors',
|
||||
|
||||
23
src/i18n.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import Vue from "vue";
|
||||
import VueI18n from "vue-i18n";
|
||||
|
||||
import config from "@/config";
|
||||
|
||||
Vue.use(VueI18n);
|
||||
|
||||
const locales = require.context("./locales", true, /[A-Za-z0-9-_,\s]+\.json$/i);
|
||||
const messages = {};
|
||||
locales.keys().forEach(key => {
|
||||
const matched = key.match(/([A-Za-z0-9-_]+)\./i);
|
||||
if (matched && matched.length > 1) {
|
||||
const locale = matched[1];
|
||||
messages[locale] = locales(key);
|
||||
}
|
||||
});
|
||||
|
||||
export default new VueI18n({
|
||||
locale: config.locale,
|
||||
fallbackLocale: "en",
|
||||
formatFallbackMessages: true,
|
||||
messages,
|
||||
});
|
||||
24
src/locales/de.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"Automatically center the map view and zoom in to relevant data": "Kartenansicht automatisch zentrieren und zu relevanten Daten hereinzoomen",
|
||||
"Fit view": "Ansicht anpassen",
|
||||
"Layer settings": "Ebeneneinstellungen",
|
||||
"Show/hide layers": "Ebenen ein-/ausblenden",
|
||||
"Select start date": "Startdatum auswählen",
|
||||
"Select end date": "Enddatum auswählen",
|
||||
"Select user": "Benutzer auswählen",
|
||||
"Show all": "Alle anzeigen",
|
||||
"Select device": "Gerät auswählen",
|
||||
"Download raw data": "Rohdaten herunterladen",
|
||||
"Information": "Information",
|
||||
"Show last known locations": "Zeige letzte bekannte Standorte",
|
||||
"Show location history (line)": "Zeige Standortverlauf (Linie)",
|
||||
"Show location history (points)": "Zeige Standortverlauf (Punkte)",
|
||||
"Show location heatmap": "Zeige Standort-Heatmap",
|
||||
"Minify JSON": "JSON minimieren",
|
||||
"Copy to clipboard": "In die Zwischenablage kopieren",
|
||||
"Download": "Herunterladen",
|
||||
"OwnTracks website": "OwnTracks Webseite",
|
||||
"OwnTracks documentation": "OwnTracks Dokumentation",
|
||||
"OwnTracks on Twitter": "OwnTracks auf Twitter",
|
||||
"Loading data, please wait...": "Daten werden geladen, bitte warten..."
|
||||
}
|
||||
24
src/locales/en.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"Automatically center the map view and zoom in to relevant data": "Automatically center the map view and zoom in to relevant data",
|
||||
"Fit view": "Fit view",
|
||||
"Layer settings": "Layer settings",
|
||||
"Show/hide layers": "Show/hide layers",
|
||||
"Select start date": "Select start date",
|
||||
"Select end date": "Select end date",
|
||||
"Select user": "Select user",
|
||||
"Show all": "Show all",
|
||||
"Select device": "Select device",
|
||||
"Download raw data": "Download raw data",
|
||||
"Information": "Information",
|
||||
"Show last known locations": "Show last known locations",
|
||||
"Show location history (line)": "Show location history (line)",
|
||||
"Show location history (points)": "Show location history (points)",
|
||||
"Show location heatmap": "Show location heatmap",
|
||||
"Minify JSON": "Minify JSON",
|
||||
"Copy to clipboard": "Copy to clipboard",
|
||||
"Download": "Download",
|
||||
"OwnTracks website": "OwnTracks website",
|
||||
"OwnTracks documentation": "OwnTracks documentation",
|
||||
"OwnTracks on Twitter": "OwnTracks on Twitter",
|
||||
"Loading data, please wait...": "Loading data, please wait..."
|
||||
}
|
||||
@@ -1,14 +1,18 @@
|
||||
import Vue from "vue";
|
||||
import App from "@/App.vue";
|
||||
import i18n from "@/i18n";
|
||||
import router from "@/router";
|
||||
import store from "@/store";
|
||||
import VModal from "vue-js-modal";
|
||||
import VOutsideEvents from "vue-outside-events";
|
||||
|
||||
Vue.use(VModal);
|
||||
Vue.use(VOutsideEvents);
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
new Vue({
|
||||
i18n,
|
||||
router,
|
||||
store,
|
||||
render: h => h(App),
|
||||
|
||||
@@ -33,9 +33,17 @@ ul {
|
||||
|
||||
input[type="checkbox"] {
|
||||
appearance: none;
|
||||
border: 0; // Remove the unchecked checkbox outline in Safari on iOS
|
||||
border-radius: 4px; // Round the focus box-shadow
|
||||
cursor: pointer;
|
||||
margin-right: 3px;
|
||||
position: relative;
|
||||
vertical-align: bottom;
|
||||
vertical-align: top;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
&::before {
|
||||
border: 2px solid var(--color-primary);
|
||||
@@ -236,7 +244,6 @@ pre {
|
||||
}
|
||||
|
||||
.dropdown-body {
|
||||
display: none;
|
||||
position: absolute;
|
||||
margin-top: 12px;
|
||||
padding: 8px 0;
|
||||
@@ -252,7 +259,7 @@ pre {
|
||||
padding: 8px 15px;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,10 +276,6 @@ pre {
|
||||
left: 20px;
|
||||
}
|
||||
|
||||
.dropdown:focus-within .dropdown-body {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.feather {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,12 @@ module.exports = {
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
||||
pluginOptions: {
|
||||
i18n: {
|
||||
locale: "en",
|
||||
fallbackLocale: "en",
|
||||
localeDir: "locales",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||