mirror of
https://github.com/Einundzwanzig-Podcast/einundzwanzig-portal.git
synced 2025-12-11 06:46:47 +00:00
huge Laravel 10 upgrade
This commit is contained in:
18
support/laravel-maps/.editorconfig
Normal file
18
support/laravel-maps/.editorconfig
Normal file
@@ -0,0 +1,18 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
|
||||
[*.js]
|
||||
indent_size = 2
|
||||
15
support/laravel-maps/.gitattributes
vendored
Normal file
15
support/laravel-maps/.gitattributes
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
* text=auto
|
||||
|
||||
/tests export-ignore
|
||||
.editorconfig export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
|
||||
*.css linguist-vendored
|
||||
*.scss linguist-vendored
|
||||
*.js linguist-vendored
|
||||
|
||||
*.jpg binary
|
||||
*.png binary
|
||||
*.gif binary
|
||||
*.ico binary
|
||||
53
support/laravel-maps/.gitignore
vendored
Normal file
53
support/laravel-maps/.gitignore
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
/.idea
|
||||
/.vscode
|
||||
.phpunit.result.cache
|
||||
|
||||
### Composer
|
||||
/vendor
|
||||
composer.phar
|
||||
|
||||
### Node
|
||||
/node_modules
|
||||
/package-lock.json
|
||||
npm-debug.log*
|
||||
.npm/
|
||||
.eslintcache
|
||||
.node_repl_history
|
||||
|
||||
*.js.LICENSE
|
||||
|
||||
### Windows
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
$RECYCLE.BIN/
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
*.lnk
|
||||
|
||||
### macOS
|
||||
*.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
._*
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Linux
|
||||
*~
|
||||
.fuse_hidden*
|
||||
.directory
|
||||
.Trash-*
|
||||
.nfs*
|
||||
44
support/laravel-maps/CHANGELOG.md
Normal file
44
support/laravel-maps/CHANGELOG.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes will be documented in this file.
|
||||
|
||||
### 1.2.2 - 2020-02-06
|
||||
|
||||
- Change the marker options iconSize and iconAnchor to snake case
|
||||
|
||||
### 1.2.1 - 2020-01-06
|
||||
|
||||
- Prevent double initialization
|
||||
|
||||
### 1.2.0 - 2020-01-06
|
||||
|
||||
- Add support for marker popups
|
||||
- Events
|
||||
|
||||
### 1.1.0 - 2020-01-06
|
||||
|
||||
- Add support for custom marker icon images
|
||||
|
||||
### 1.0.4 - 2020-01-06
|
||||
|
||||
- Add Laravel 6 compatibility
|
||||
|
||||
### 1.0.3 - 2019-04-18
|
||||
|
||||
- Update service provider
|
||||
|
||||
### 1.0.2 - 2018-10-25
|
||||
|
||||
- Code cleanup
|
||||
|
||||
### 1.0.1 - 2018-08-30
|
||||
|
||||
- Code cleanup
|
||||
|
||||
## 1.0.0 - 2018-08-30
|
||||
|
||||
- Improvements, first stable release
|
||||
|
||||
## 0.0.1 - 2018-08-30
|
||||
|
||||
- Initial beta release
|
||||
77
support/laravel-maps/CONTRIBUTING.md
Normal file
77
support/laravel-maps/CONTRIBUTING.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Contributing
|
||||
|
||||
Contributions are **welcome** and will be fully **credited**.
|
||||
|
||||
Please read and understand the contribution guide before creating an issue or
|
||||
pull request.
|
||||
|
||||
## Etiquette
|
||||
|
||||
This project is open source, and as such, the maintainers give their free time
|
||||
to build and maintain the source code held within. They make the code freely
|
||||
available in the hope that it will be of use to other developers. It would be
|
||||
extremely unfair for them to suffer abuse or anger for their hard work.
|
||||
|
||||
Please be considerate towards maintainers when raising issues or presenting
|
||||
pull requests. Let's show the world that developers are civilized and selfless
|
||||
people.
|
||||
|
||||
It's the duty of the maintainer to ensure that all submissions to the project
|
||||
are of sufficient quality to benefit the project. Many developers have
|
||||
different skillsets, strengths, and weaknesses. Respect the maintainer's
|
||||
decision, and do not be upset or abusive if your submission is not used.
|
||||
|
||||
## Viability
|
||||
|
||||
When requesting or submitting new features, first consider whether it might be
|
||||
useful to others. Open source projects are used by many developers, who may
|
||||
have entirely different needs to your own. Think about whether or not your
|
||||
feature is likely to be used by other users of the project.
|
||||
|
||||
## Procedure
|
||||
|
||||
Before filing an issue:
|
||||
|
||||
- Attempt to replicate the problem, to ensure that it wasn't a coincidental
|
||||
incident.
|
||||
|
||||
- Check to make sure your feature suggestion isn't already present within the
|
||||
project.
|
||||
|
||||
- Check the pull requests tab to ensure that the bug doesn't have a fix in
|
||||
progress.
|
||||
|
||||
- Check the pull requests tab to ensure that the feature isn't already in
|
||||
progress.
|
||||
|
||||
|
||||
Before submitting a pull request:
|
||||
|
||||
- Check the codebase to ensure that your feature doesn't already exist.
|
||||
- Check the pull requests to ensure that another person hasn't already
|
||||
submitted the feature or fix.
|
||||
|
||||
## Requirements
|
||||
|
||||
If the project maintainer has any additional requirements, you will find them
|
||||
listed here.
|
||||
|
||||
- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer).
|
||||
|
||||
- **Add tests!** - Your patch won't be accepted if it doesn't have tests.
|
||||
|
||||
- **Document any change in behaviour** - Make sure the `README.md` and any
|
||||
other relevant documentation are kept up-to-date.
|
||||
|
||||
- **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/).
|
||||
Randomly breaking public APIs is not an option.
|
||||
|
||||
- **One pull request per feature** - If you want to do more than one thing,
|
||||
send multiple pull requests.
|
||||
|
||||
- **Send coherent history** - Make sure each individual commit in your pull
|
||||
request is meaningful. If you had to make multiple intermediate commits while
|
||||
developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages)
|
||||
before submitting.
|
||||
|
||||
**Happy coding**!
|
||||
21
support/laravel-maps/LICENSE.md
Normal file
21
support/laravel-maps/LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-present Go NoWare
|
||||
|
||||
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.
|
||||
266
support/laravel-maps/README.md
Normal file
266
support/laravel-maps/README.md
Normal file
@@ -0,0 +1,266 @@
|
||||
# Maps for your Laravel application
|
||||
|
||||
[](https://gitlab.com/gonoware/laravel-maps)
|
||||
[]()
|
||||
[](https://packagist.org/packages/gonoware/laravel-maps)
|
||||
[](https://gitlab.styleci.io/repos/8146646)
|
||||
[](https://packagist.org/packages/gonoware/laravel-maps)
|
||||
[](https://packagist.org/packages/gonoware/laravel-maps)
|
||||
|
||||
Using this package you can easily display maps on your website.
|
||||
|
||||
Supported map services:
|
||||
* Google Maps
|
||||
* OpenStreetMap
|
||||
* Bing Maps
|
||||
* MapQuest
|
||||
* Yandex Maps
|
||||
* MapKit (beta)
|
||||
|
||||
> Note: Yandex Maps API does not work in Chrome.
|
||||
|
||||
## Features
|
||||
| | Google Maps | OpenStreetMap | Bing Maps | MapQuest | Yandex Maps | MapKit |
|
||||
|---|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| [Map](#basic-map) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
|
||||
| [Markers](#map-with-markers) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
|
||||
| [Marker Links](#marker-links) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
|
||||
| [Marker Popups](#marker-popups) | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ |
|
||||
| [Custom Marker Icons](#custom-marker-icons) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
|
||||
| [Marker Click Event](#marker-clicked) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
|
||||
|
||||
## Installation
|
||||
|
||||
This package can be installed through Composer.
|
||||
```bash
|
||||
composer require gonoware/laravel-maps
|
||||
```
|
||||
|
||||
Publish the compiled assets to `public/vendor/maps` with one of these
|
||||
commands:
|
||||
```bash
|
||||
php artisan vendor:publish --tag=maps
|
||||
```
|
||||
```bash
|
||||
php artisan vendor:publish --provider="GoNoWare\Maps\MapsServiceProvider" --tag=public
|
||||
```
|
||||
> When updating, use the `--force` switch to overwrite existing assets:
|
||||
```bash
|
||||
php artisan vendor:publish --tag=maps --force
|
||||
```
|
||||
|
||||
Optionally, you can also publish the config file of this package with this
|
||||
command to `config/vendor/maps.php`:
|
||||
```bash
|
||||
php artisan vendor:publish --provider="GoNoWare\Maps\MapsServiceProvider" --tag=config
|
||||
```
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Load the map styles by adding the following directive to your
|
||||
Blade template before the `</head>` closing tag.
|
||||
```php
|
||||
@mapstyles
|
||||
```
|
||||
|
||||
Then add the following directive to your Blade template
|
||||
before the `</body>` closing tag, to load the map scripts.
|
||||
```php
|
||||
@mapscripts
|
||||
```
|
||||
|
||||
### Basic Map
|
||||
Display a map by adding the `@map` directive to your Blade template.
|
||||
```php
|
||||
@map([
|
||||
'lat' => 48.134664,
|
||||
'lng' => 11.555220,
|
||||
'zoom' => 6,
|
||||
])
|
||||
```
|
||||
|
||||
### Map With Markers
|
||||
You can also show markers / pins / annotations:
|
||||
```php
|
||||
@map([
|
||||
'lat' => 48.134664,
|
||||
'lng' => 11.555220,
|
||||
'zoom' => 6,
|
||||
'markers' => [
|
||||
[
|
||||
'title' => 'Go NoWare',
|
||||
'lat' => 48.134664,
|
||||
'lng' => 11.555220,
|
||||
],
|
||||
],
|
||||
])
|
||||
```
|
||||
|
||||
### Marker Links
|
||||
Open a url when a marker is clicked.
|
||||
```php
|
||||
@map([
|
||||
'lat' => 48.134664,
|
||||
'lng' => 11.555220,
|
||||
'zoom' => 6,
|
||||
'markers' => [
|
||||
[
|
||||
'title' => 'Go NoWare',
|
||||
'lat' => 48.134664,
|
||||
'lng' => 11.555220,
|
||||
'url' => 'https://gonoware.com',
|
||||
],
|
||||
],
|
||||
])
|
||||
```
|
||||
|
||||
### Marker Popups
|
||||
Show a popup when a marker is clicked. The `popup` attribute may contain HTML markup.
|
||||
```php
|
||||
@map([
|
||||
'lat' => 48.134664,
|
||||
'lng' => 11.555220,
|
||||
'zoom' => 6,
|
||||
'markers' => [
|
||||
[
|
||||
'title' => 'Go NoWare',
|
||||
'lat' => 48.134664,
|
||||
'lng' => 11.555220,
|
||||
'popup' => '<h3>Details</h3><p>Click <a href="https://gonoware.com">here</a>.</p>',
|
||||
],
|
||||
],
|
||||
])
|
||||
```
|
||||
|
||||
### Custom Marker Icons
|
||||
Show a custom marker icon. Absolute and relative URLs are supported.
|
||||
```php
|
||||
@map([
|
||||
'lat' => 48.134664,
|
||||
'lng' => 11.555220,
|
||||
'zoom' => 6,
|
||||
'markers' => [
|
||||
[
|
||||
'title' => 'Go NoWare',
|
||||
'lat' => 48.134664,
|
||||
'lng' => 11.555220,
|
||||
'url' => 'https://gonoware.com',
|
||||
'icon' => 'https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png',
|
||||
],
|
||||
],
|
||||
])
|
||||
```
|
||||
|
||||
Additionally you may also specify the icon image size and anchor in pixels. The image will be aligned so that the tip of the icon is at the marker's geographical location.
|
||||
```php
|
||||
@map([
|
||||
'lat' => 48.134664,
|
||||
'lng' => 11.555220,
|
||||
'zoom' => 6,
|
||||
'markers' => [
|
||||
[
|
||||
'title' => 'Go NoWare',
|
||||
'lat' => 48.134664,
|
||||
'lng' => 11.555220,
|
||||
'url' => 'https://gonoware.com',
|
||||
'icon' => 'https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png',
|
||||
'icon_size' => [20, 32],
|
||||
'icon_anchor' => [0, 32],
|
||||
],
|
||||
],
|
||||
])
|
||||
```
|
||||
|
||||
## Styling
|
||||
|
||||
To adjust the height of the map use CSS:
|
||||
```css
|
||||
.gnw-map-service {
|
||||
height: 750px;
|
||||
}
|
||||
```
|
||||
|
||||
Change the background of the map container:
|
||||
```css
|
||||
.gnw-map-service__osm {
|
||||
background: rgb(221, 221, 221);
|
||||
}
|
||||
```
|
||||
|
||||
Fade in by default when using Bootstrap 3.3.7 or 4+. To replicate or modify the animation use following CSS:
|
||||
```css
|
||||
.gnw-map.fade {
|
||||
transition: opacity .15s linear;
|
||||
}
|
||||
.gnw-map.fade:not(.show) {
|
||||
opacity: 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
### Map Initialized
|
||||
The event `LaravelMaps:MapInitialized` will be dispatched when a map and its markers were initialized. The DOM element, map,
|
||||
markers and service name can be accessed via the event details.
|
||||
```js
|
||||
window.addEventListener('LaravelMaps:MapInitialized', function (event) {
|
||||
var element = event.detail.element;
|
||||
var map = event.detail.map;
|
||||
var markers = event.detail.markers;
|
||||
var service = event.detail.service;
|
||||
console.log('map initialized', element, map, markers, service);
|
||||
});
|
||||
```
|
||||
Please refer to the respective documentation for advanced customization:
|
||||
* [Google Maps](https://developers.google.com/maps/documentation/javascript/tutorial)
|
||||
* [OpenStreetMap](https://leafletjs.com/reference-1.6.0.html)
|
||||
* [Bing Maps](https://leafletjs.com/reference-1.6.0.html)
|
||||
* [MapQuest](https://leafletjs.com/reference-1.6.0.html)
|
||||
* [Yandex Maps](https://tech.yandex.com/maps/jsapi/)
|
||||
* [MapKit (beta)](https://developer.apple.com/documentation/mapkitjs)
|
||||
|
||||
### Marker Clicked
|
||||
The event `LaravelMaps:MarkerClicked` will be dispatched when a marker was clicked. The DOM element, map, marker and
|
||||
service name can be accessed via the event details.
|
||||
```js
|
||||
window.addEventListener('LaravelMaps:MarkerClicked', function (event) {
|
||||
var element = event.detail.element;
|
||||
var map = event.detail.map;
|
||||
var marker = event.detail.marker;
|
||||
var service = event.detail.service;
|
||||
console.log('marker clicked', element, map, marker, service);
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## Changelog
|
||||
|
||||
Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
|
||||
|
||||
|
||||
## Security
|
||||
|
||||
If you discover any security related issues, please email [em@gonoware.com](mailto:em@gonoware.com)
|
||||
instead of using the issue tracker.
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
- [Emanuel Mutschlechner](https://gitlab.com/emanuel)
|
||||
- [Benedikt Tuschter](https://gitlab.com/benedikttuschter)
|
||||
- [All Contributors](https://gitlab.com/gonoware/laravel-maps/graphs/master)
|
||||
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE.md)
|
||||
|
||||
Copyright (c) 2018-present Go NoWare
|
||||
|
||||
[](https://app.fossa.io/projects/git%2Bgitlab.com%2Fgonoware%2Flaravel-maps?ref=badge_large)
|
||||
82
support/laravel-maps/composer.json
Normal file
82
support/laravel-maps/composer.json
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"name": "gonoware/laravel-maps",
|
||||
"description": "Maps for your Laravel application",
|
||||
"keywords": [
|
||||
"maps",
|
||||
"laravel",
|
||||
"gonoware",
|
||||
"osm",
|
||||
"google",
|
||||
"bing",
|
||||
"yandex",
|
||||
"mapquest",
|
||||
"googlemaps",
|
||||
"openstreetmap",
|
||||
"bingmaps",
|
||||
"yandexmaps",
|
||||
"mapkit",
|
||||
"mapkitjs"
|
||||
],
|
||||
"homepage": "https://gitlab.com/gonoware/laravel-maps",
|
||||
"support": {
|
||||
"issues": "https://gitlab.com/gonoware/laravel-maps/issues",
|
||||
"source": "https://gitlab.com/gonoware/laravel-maps"
|
||||
},
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Emanuel Mutschlechner",
|
||||
"email": "em@gonoware.com"
|
||||
},
|
||||
{
|
||||
"name": "Benedikt Tuschter",
|
||||
"email": "bt@gonoware.com"
|
||||
}
|
||||
],
|
||||
"type": "library",
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"illuminate/support": "^9.0|^10.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.4.4",
|
||||
"phpunit/phpunit": "^9.5.10"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GoNoWare\\Maps\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"GoNoWare\\Maps\\MapsServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build-script": [
|
||||
"yarn install",
|
||||
"yarn run prod"
|
||||
],
|
||||
"post-install-cmd": [
|
||||
"@build-script"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@build-script"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
2854
support/laravel-maps/composer.lock
generated
Normal file
2854
support/laravel-maps/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
93
support/laravel-maps/config/maps.php
Normal file
93
support/laravel-maps/config/maps.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|----------------------------------------------------------------------
|
||||
| Default Map Service Name
|
||||
|----------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify which of the map services below you wish to use
|
||||
| as your default service for all displayed maps. Of course you may use many services at once using the Maps library.
|
||||
| Available maps: 'google', 'osm', 'bing', 'mapquest', 'yandex', 'mapkit'
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('MAPS_SERVICE', 'osm'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Map Services
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here are each of the map services setup for your application.
|
||||
| Of course, examples of configuring each map api that is supported by
|
||||
| Maps is shown below to make development simple.
|
||||
|
|
||||
|
|
||||
| All proprietary map services require an API Key, so make sure you have
|
||||
| the key for your particular service of choice defined in your .env
|
||||
| before you begin development.
|
||||
|
|
||||
*/
|
||||
|
||||
'services' => [
|
||||
|
||||
'google' => [
|
||||
// https://developers.google.com/maps/documentation/javascript/get-api-key
|
||||
// https://developers.google.com/maps/documentation/embed/get-api-key
|
||||
'key' => env('MAPS_GOOGLE_KEY', ''),
|
||||
|
||||
// https://developers.google.com/maps/documentation/javascript/maptypes
|
||||
'type' => 'roadmap', // 'roadmap', 'satellite', 'hybrid', 'terrain',
|
||||
],
|
||||
|
||||
'bing' => [
|
||||
// https://msdn.microsoft.com/en-us/library/ff428642.aspx
|
||||
// https://www.bingmapsportal.com
|
||||
'key' => env('MAPS_BING_KEY', ''),
|
||||
],
|
||||
|
||||
'osm' => [
|
||||
'type' => 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
],
|
||||
|
||||
'yandex' => [
|
||||
// https://developer.tech.yandex.com/keys
|
||||
// limited free quota
|
||||
'key' => env('MAPS_YANDEX_KEY', ''),
|
||||
],
|
||||
|
||||
'mapquest' => [
|
||||
// https://developer.mapquest.com/plan_purchase/steps/business_edition/business_edition_free/register
|
||||
// https://developer.mapquest.com/user/me/apps
|
||||
'key' => env('MAPS_MAPQUEST_KEY', ''),
|
||||
|
||||
// https://developer.mapquest.com/documentation/mapquest-js/v1.3/l-mapquest-tile-layer/
|
||||
'type' => 'map', // 'map', 'hybrid', 'satellite', 'light', 'dark'
|
||||
],
|
||||
|
||||
'mapkit' => [
|
||||
// https://developer.apple.com/videos/play/wwdc2018/508
|
||||
// https://developer.apple.com/documentation/mapkitjs/setting_up_mapkit_js?changes=latest_minor
|
||||
'key' => env('MAPS_MAPKIT_KEY', ''),
|
||||
|
||||
// https://developer.apple.com/documentation/mapkitjs/mapkit/map/maptypes
|
||||
'type' => 'standard', // 'standard', 'hybrid', 'satellite'
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Maps Enabled
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default, Maps is enabled. You can set the value to false to disable
|
||||
| rendering of all maps.
|
||||
|
|
||||
*/
|
||||
|
||||
'enabled' => env('MAPS_ENABLED', true),
|
||||
|
||||
];
|
||||
32
support/laravel-maps/package.json
Normal file
32
support/laravel-maps/package.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npm run development",
|
||||
"development": "mix",
|
||||
"watch": "mix watch",
|
||||
"watch-poll": "mix watch -- --watch-options-poll=1000",
|
||||
"hot": "mix watch --hot",
|
||||
"prod": "npm run production",
|
||||
"production": "mix --production"
|
||||
},
|
||||
"devDependencies": {
|
||||
"laravel-mix": "^6.0.6",
|
||||
"leaflet-bing-layer": "^3.3.1",
|
||||
"resolve-url-loader": "^3.1.0",
|
||||
"sass": "^1.24.2",
|
||||
"sass-loader": "^8.0.0",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 1 version",
|
||||
"> 1%",
|
||||
"Chrome >= 45",
|
||||
"Firefox >= 38",
|
||||
"Edge >= 12",
|
||||
"Explorer >= 10",
|
||||
"iOS >= 9",
|
||||
"Safari >= 9",
|
||||
"Android >= 4.4",
|
||||
"Opera >= 30"
|
||||
]
|
||||
}
|
||||
1
support/laravel-maps/public/css/index.css
vendored
Normal file
1
support/laravel-maps/public/css/index.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.gnw-map-service{height:400px;position:relative}.gnw-map-service__google{background:#e5e3df}.gnw-map-service__bing,.gnw-map-service__mapquest,.gnw-map-service__osm{background:#ddd}.gnw-map-service__yandex{background:#f3f1ed}.gnw-map-service__mapkit{background:#f8f4ec}.gnw-map{height:inherit}.gnw-map .mk-map-view>.syrup-canvas{margin-left:-50%}
|
||||
2
support/laravel-maps/public/js/index.js
vendored
Normal file
2
support/laravel-maps/public/js/index.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4
support/laravel-maps/public/js/index.js.LICENSE.txt
Normal file
4
support/laravel-maps/public/js/index.js.LICENSE.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
/* @preserve
|
||||
* Leaflet 1.9.2, a JS library for interactive maps. https://leafletjs.com
|
||||
* (c) 2010-2022 Vladimir Agafonkin, (c) 2010-2011 CloudMade
|
||||
*/
|
||||
4
support/laravel-maps/public/mix-manifest.json
Normal file
4
support/laravel-maps/public/mix-manifest.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"/js/index.js": "/js/index.js?id=6508504506f20e7f033c7de36683001a",
|
||||
"/css/index.css": "/css/index.css?id=56062e68c820fdf21c3c4ec98fbe553b"
|
||||
}
|
||||
69
support/laravel-maps/resources/js/index.js
vendored
Normal file
69
support/laravel-maps/resources/js/index.js
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
import google from './services/google';
|
||||
import osm from './services/osm';
|
||||
import bing from './services/bing';
|
||||
import mapquest from './services/mapquest';
|
||||
import yandex from './services/yandex';
|
||||
import mapkit from './services/mapkit';
|
||||
|
||||
import parser from './utils/parser';
|
||||
import {isDefined, logError} from './utils/helper';
|
||||
import './utils/customEventPolyfill';
|
||||
import {dispatchEventMapInitialized} from './utils/dispatchEvent';
|
||||
|
||||
const createMap = (element, createMap, createMarker) => {
|
||||
if (!isDefined(element)) {
|
||||
logError('element is undefined');
|
||||
return;
|
||||
}
|
||||
const mapData = parser.map(element);
|
||||
if (!isDefined(mapData)) {
|
||||
logError('map data is undefined');
|
||||
return;
|
||||
}
|
||||
const map = createMap(element, mapData);
|
||||
if (!isDefined(map)) {
|
||||
logError('map is undefined');
|
||||
return;
|
||||
}
|
||||
|
||||
const markers = mapData.markers.map(markerData => createMarker(element, map, markerData));
|
||||
|
||||
return {
|
||||
map,
|
||||
markers,
|
||||
};
|
||||
};
|
||||
|
||||
const createMapService = service => {
|
||||
const createMapService = element => createMap(
|
||||
element,
|
||||
service.createMap,
|
||||
service.createMarker,
|
||||
);
|
||||
const selector = `[data-map-${service.name}]`;
|
||||
const elements = Array.prototype.slice.call(document.querySelectorAll(selector) || []);
|
||||
elements.forEach(element => {
|
||||
if (element.getAttribute('data-map-initialized') === true) {
|
||||
return;
|
||||
}
|
||||
const data = createMapService(element);
|
||||
dispatchEventMapInitialized(service.name, element, data);
|
||||
element.setAttribute('data-map-initialized', true);
|
||||
});
|
||||
};
|
||||
|
||||
window.onGoogleMapsReady = () => createMapService(google);
|
||||
|
||||
window.onYandexMapsReady = () => createMapService(yandex);
|
||||
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
(() => createMapService(osm))();
|
||||
|
||||
(() => createMapService(bing))();
|
||||
|
||||
(() => createMapService(mapquest))();
|
||||
|
||||
(() => createMapService(mapkit))();
|
||||
});
|
||||
|
||||
|
||||
33
support/laravel-maps/resources/js/services/bing.js
vendored
Normal file
33
support/laravel-maps/resources/js/services/bing.js
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
import {fadeElementIn, isDefined, logError, openUrl} from '../utils/helper';
|
||||
import 'leaflet-bing-layer';
|
||||
import {createMarker} from '../utils/leaflet';
|
||||
|
||||
const name = 'bing';
|
||||
|
||||
export default {
|
||||
name,
|
||||
createMap(element, mapData) {
|
||||
if (!isDefined(window.L)) {
|
||||
logError('leaflet is undefined');
|
||||
return;
|
||||
}
|
||||
const {lat, lng, zoom, service} = mapData;
|
||||
|
||||
const map = window.L
|
||||
.map(element, {})
|
||||
.on('load', () => {
|
||||
fadeElementIn(element);
|
||||
})
|
||||
.setView([lat, lng], zoom);
|
||||
|
||||
window.L.tileLayer
|
||||
.bing({
|
||||
bingMapsKey: service.key,
|
||||
imagerySet: 'CanvasLight',
|
||||
})
|
||||
.addTo(map);
|
||||
|
||||
return map;
|
||||
},
|
||||
createMarker: createMarker.bind(null, name),
|
||||
}
|
||||
70
support/laravel-maps/resources/js/services/google.js
vendored
Normal file
70
support/laravel-maps/resources/js/services/google.js
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
import {fadeElementIn, isDefined, logError, openUrl} from '../utils/helper';
|
||||
import {dispatchEventMarkerClicked} from '../utils/dispatchEvent';
|
||||
|
||||
const name = 'google';
|
||||
|
||||
export default {
|
||||
name,
|
||||
createMap(element, mapData) {
|
||||
if (!isDefined(window.google)) {
|
||||
logError('google is undefined');
|
||||
return;
|
||||
}
|
||||
if (!isDefined(window.google.maps)) {
|
||||
logError('google maps is undefined');
|
||||
return;
|
||||
}
|
||||
|
||||
const {lat, lng, zoom, service} = mapData;
|
||||
|
||||
const map = new window.google.maps.Map(element, {
|
||||
center: new window.google.maps.LatLng(lat, lng),
|
||||
zoom,
|
||||
mapTypeId: service.type || window.google.maps.MapTypeId.ROADMAP,
|
||||
});
|
||||
|
||||
window.google.maps.event.addListenerOnce(map, 'idle', () => {
|
||||
fadeElementIn(element);
|
||||
});
|
||||
|
||||
return map;
|
||||
},
|
||||
createMarker(element, map, markerData) {
|
||||
const {title, lat, lng, url, popup, icon, iconSize, iconAnchor} = markerData;
|
||||
|
||||
const markerOptions = {
|
||||
position: new window.google.maps.LatLng(lat, lng),
|
||||
map,
|
||||
title,
|
||||
draggable: false,
|
||||
};
|
||||
|
||||
if (icon) {
|
||||
markerOptions.icon = {
|
||||
url: icon,
|
||||
};
|
||||
if (iconSize) {
|
||||
markerOptions.icon.size = new window.google.maps.Size(...iconSize);
|
||||
}
|
||||
if (iconAnchor) {
|
||||
markerOptions.icon.anchor = new window.google.maps.Point(...iconAnchor);
|
||||
}
|
||||
}
|
||||
|
||||
const marker = new window.google.maps.Marker(markerOptions);
|
||||
|
||||
marker.addListener('click', () => {
|
||||
dispatchEventMarkerClicked(name, element, map, marker);
|
||||
if (popup) {
|
||||
const infoWindow = new window.google.maps.InfoWindow({
|
||||
content: popup
|
||||
});
|
||||
infoWindow.open(map, marker);
|
||||
} else if (url) {
|
||||
openUrl(url);
|
||||
}
|
||||
});
|
||||
|
||||
return marker;
|
||||
},
|
||||
};
|
||||
66
support/laravel-maps/resources/js/services/mapkit.js
vendored
Normal file
66
support/laravel-maps/resources/js/services/mapkit.js
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
import {fadeElementIn, isDefined, logError, openUrl} from '../utils/helper';
|
||||
import {dispatchEventMarkerClicked} from "../utils/dispatchEvent";
|
||||
|
||||
const name = 'mapkit';
|
||||
|
||||
export default {
|
||||
name,
|
||||
createMap(element, mapData) {
|
||||
if (!isDefined(window.mapkit)) {
|
||||
logError('mapkit is undefined');
|
||||
return;
|
||||
}
|
||||
const {lat, lng, zoom, service} = mapData;
|
||||
|
||||
window.mapkit.init({
|
||||
authorizationCallback(done) {
|
||||
done(service.key);
|
||||
},
|
||||
});
|
||||
window.mapkit.addEventListener('configuration-change', event => {
|
||||
if (event.status === 'Initialized') {
|
||||
fadeElementIn(element);
|
||||
}
|
||||
});
|
||||
|
||||
const map = new window.mapkit.Map(element, {
|
||||
mapType: service.type || window.mapkit.Map.MapTypes.Standard,
|
||||
});
|
||||
|
||||
const delta = Math.exp(Math.log(360) - (zoom * Math.LN2)); // TODO: zoom to delta not working
|
||||
map.region = new window.mapkit.CoordinateRegion(
|
||||
new window.mapkit.Coordinate(lat, lng),
|
||||
new window.mapkit.CoordinateSpan(delta, delta),
|
||||
);
|
||||
|
||||
return map;
|
||||
},
|
||||
createMarker(element, map, markerData) {
|
||||
const {title, lat, lng, url, popup, icon} = markerData;
|
||||
|
||||
const coordinate = new window.mapkit.Coordinate(lat, lng);
|
||||
|
||||
const markerAnnotationOptions = {title};
|
||||
|
||||
if (icon) {
|
||||
markerAnnotationOptions.glyphImage = {
|
||||
1: icon,
|
||||
};
|
||||
}
|
||||
|
||||
const marker = new window.mapkit.MarkerAnnotation(coordinate, markerAnnotationOptions);
|
||||
|
||||
marker.addEventListener('select', event => {
|
||||
dispatchEventMarkerClicked(name, element, map, marker);
|
||||
if (popup) {
|
||||
// TODO
|
||||
} else if (url) {
|
||||
openUrl(url);
|
||||
}
|
||||
});
|
||||
|
||||
map.showItems([marker]); // TODO: map auto resize bugging if multiple markers
|
||||
|
||||
return marker;
|
||||
},
|
||||
};
|
||||
40
support/laravel-maps/resources/js/services/mapquest.js
vendored
Normal file
40
support/laravel-maps/resources/js/services/mapquest.js
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
import {fadeElementIn, isDefined, logError, openUrl} from '../utils/helper';
|
||||
import {createMarker} from '../utils/leaflet';
|
||||
|
||||
// TODO maybe add this https://github.com/elmarquis/Leaflet.GestureHandling/
|
||||
|
||||
// TODO add config for different styles like database connections: https://wiki.openstreetmap.org/wiki/Tile_servers
|
||||
// http://leaflet-extras.github.io/leaflet-providers/preview/
|
||||
|
||||
// TODO custom icons: https://leafletjs.com/examples/custom-icons/
|
||||
const name = 'mapquest';
|
||||
|
||||
export default {
|
||||
name,
|
||||
createMap(element, mapData) {
|
||||
if (!isDefined(window.L)) {
|
||||
logError('leaflet is undefined');
|
||||
return;
|
||||
}
|
||||
if (!isDefined(window.L.mapquest)) {
|
||||
logError('mapquest is undefined');
|
||||
return
|
||||
}
|
||||
const {lat, lng, zoom, service} = mapData;
|
||||
window.L.mapquest.key = service.key;
|
||||
|
||||
const map = window.L.mapquest
|
||||
.map(element, {
|
||||
center: [lat, lng],
|
||||
zoom,
|
||||
layers: window.L.mapquest.tileLayer(service.type || 'map'),
|
||||
})
|
||||
.on('load', () => {
|
||||
fadeElementIn(element);
|
||||
})
|
||||
.setView([lat, lng], zoom);
|
||||
|
||||
return map;
|
||||
},
|
||||
createMarker: createMarker.bind(null, name),
|
||||
}
|
||||
38
support/laravel-maps/resources/js/services/osm.js
vendored
Normal file
38
support/laravel-maps/resources/js/services/osm.js
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
import {fadeElementIn, isDefined, logError, openUrl} from '../utils/helper';
|
||||
import {createMarker} from '../utils/leaflet';
|
||||
|
||||
// TODO maybe add this https://github.com/elmarquis/Leaflet.GestureHandling/
|
||||
|
||||
// TODO add config for different styles like database connections: https://wiki.openstreetmap.org/wiki/Tile_servers
|
||||
// http://leaflet-extras.github.io/leaflet-providers/preview/
|
||||
|
||||
// TODO custom icons: https://leafletjs.com/examples/custom-icons/
|
||||
|
||||
const name = 'osm';
|
||||
|
||||
export default {
|
||||
name,
|
||||
createMap(element, mapData) {
|
||||
if (!isDefined(window.L)) {
|
||||
logError('leaflet is undefined');
|
||||
return;
|
||||
}
|
||||
const {lat, lng, zoom, service} = mapData;
|
||||
|
||||
const map = window.L
|
||||
.map(element, {})
|
||||
.on('load', () => {
|
||||
fadeElementIn(element);
|
||||
})
|
||||
.setView([lat, lng], zoom);
|
||||
|
||||
window.L
|
||||
.tileLayer(service.type || 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
})
|
||||
.addTo(map);
|
||||
|
||||
return map;
|
||||
},
|
||||
createMarker: createMarker.bind(null, name),
|
||||
}
|
||||
65
support/laravel-maps/resources/js/services/yandex.js
vendored
Normal file
65
support/laravel-maps/resources/js/services/yandex.js
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
import {isDefined, logError, openUrl} from '../utils/helper';
|
||||
import {dispatchEventMarkerClicked} from '../utils/dispatchEvent';
|
||||
|
||||
const name = 'yandex';
|
||||
|
||||
export default {
|
||||
name,
|
||||
createMap(element, mapData) {
|
||||
if (!isDefined(window.ymaps)) {
|
||||
logError('ymaps is undefined');
|
||||
return;
|
||||
}
|
||||
const {lat, lng, zoom} = mapData;
|
||||
|
||||
const map = new window.ymaps.Map(element, {
|
||||
center: [lat, lng],
|
||||
zoom,
|
||||
});
|
||||
|
||||
// window.google.maps.event.addListenerOnce(map, 'idle', () => {
|
||||
// fadeElementIn(element);
|
||||
// });
|
||||
|
||||
return map;
|
||||
},
|
||||
createMarker(element, map, markerData) {
|
||||
const {title, lat, lng, url, popup, icon, iconSize, iconAnchor} = markerData;
|
||||
|
||||
const placemarkProperties = {
|
||||
hintContent: title,
|
||||
};
|
||||
|
||||
const placemarkOptions = {};
|
||||
|
||||
if (icon) {
|
||||
placemarkOptions.iconLayout = 'default#imageWithContent';
|
||||
placemarkOptions.iconImageHref = icon;
|
||||
if (iconSize) {
|
||||
placemarkOptions.iconImageSize = iconSize;
|
||||
}
|
||||
if (iconAnchor) {
|
||||
placemarkOptions.iconImageOffset = iconAnchor;
|
||||
}
|
||||
}
|
||||
|
||||
if (popup) {
|
||||
placemarkProperties.balloonContentBody = popup;
|
||||
}
|
||||
|
||||
const marker = new window.ymaps.Placemark([lat, lng], placemarkProperties, placemarkOptions);
|
||||
|
||||
marker.events.add('click', e => {
|
||||
dispatchEventMarkerClicked(name, element, map, marker);
|
||||
if (popup) {
|
||||
// Handled with ballonContentBody
|
||||
} else if (url) {
|
||||
openUrl(url);
|
||||
}
|
||||
});
|
||||
|
||||
map.geoObjects.add(marker);
|
||||
|
||||
return marker;
|
||||
},
|
||||
};
|
||||
15
support/laravel-maps/resources/js/utils/customEventPolyfill.js
vendored
Normal file
15
support/laravel-maps/resources/js/utils/customEventPolyfill.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
(function () {
|
||||
if (window.CustomEvent) return false;
|
||||
function CustomEvent(event, params) {
|
||||
params = params || {
|
||||
bubbles: false, cancelable: false, detail: undefined
|
||||
};
|
||||
var evt = document.createEvent( 'CustomEvent' );
|
||||
evt.initCustomEvent(
|
||||
event, params.bubbles, params.cancelable, params.detail
|
||||
);
|
||||
return evt;
|
||||
}
|
||||
CustomEvent.prototype = window.Event.prototype;
|
||||
window.CustomEvent = CustomEvent;
|
||||
})();
|
||||
23
support/laravel-maps/resources/js/utils/dispatchEvent.js
vendored
Normal file
23
support/laravel-maps/resources/js/utils/dispatchEvent.js
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
export function dispatchEventMapInitialized (serviceName, element, data) {
|
||||
const event = new CustomEvent('LaravelMaps:MapInitialized', {
|
||||
detail: {
|
||||
element: element,
|
||||
map: data.map,
|
||||
markers: data.markers || [],
|
||||
service: serviceName,
|
||||
},
|
||||
});
|
||||
window.dispatchEvent(event);
|
||||
}
|
||||
|
||||
export function dispatchEventMarkerClicked (serviceName, element, map, marker) {
|
||||
const event = new CustomEvent('LaravelMaps:MarkerClicked', {
|
||||
detail: {
|
||||
element: element,
|
||||
map: map,
|
||||
marker: marker,
|
||||
service: serviceName,
|
||||
},
|
||||
});
|
||||
window.dispatchEvent(event);
|
||||
}
|
||||
11
support/laravel-maps/resources/js/utils/helper.js
vendored
Normal file
11
support/laravel-maps/resources/js/utils/helper.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
export const isDefined = object => typeof object !== 'undefined';
|
||||
|
||||
export const fadeElementIn = element => {
|
||||
const target = element.closest('.fade');
|
||||
target.classList.add('show'); // Bootstrap 4
|
||||
target.classList.add('in'); // Backwards compatibility Bootstrap 3.3.7
|
||||
};
|
||||
|
||||
export const openUrl = url => window.open(url, '_blank');
|
||||
|
||||
export const logError = error => isDefined(console) && console.error('[laravel-maps] error:', error);
|
||||
44
support/laravel-maps/resources/js/utils/leaflet.js
vendored
Normal file
44
support/laravel-maps/resources/js/utils/leaflet.js
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
import {openUrl} from './helper';
|
||||
import {dispatchEventMarkerClicked} from './dispatchEvent';
|
||||
|
||||
export function createMarker(service, element, map, markerData) {
|
||||
const {title, lat, lng, url, popup, icon, iconSize, iconAnchor} = markerData;
|
||||
|
||||
const markerOptions = {
|
||||
title,
|
||||
keyboard: false,
|
||||
draggable: false,
|
||||
};
|
||||
|
||||
if (icon) {
|
||||
const iconOptions = {
|
||||
iconUrl: icon,
|
||||
};
|
||||
if (iconSize) {
|
||||
iconOptions.iconSize = iconSize;
|
||||
}
|
||||
if (iconAnchor) {
|
||||
iconOptions.iconAnchor = iconAnchor;
|
||||
}
|
||||
markerOptions.icon = window.L.icon(iconOptions);
|
||||
}
|
||||
|
||||
const marker = window.L.marker([lat, lng], markerOptions);
|
||||
|
||||
marker.on('click', event => {
|
||||
event.originalEvent.preventDefault();
|
||||
dispatchEventMarkerClicked(service, element, map, marker);
|
||||
if (popup) {
|
||||
window.L.popup()
|
||||
.setLatLng([lat, lng])
|
||||
.setContent(popup)
|
||||
.openOn(map);
|
||||
} else if (url) {
|
||||
openUrl(url);
|
||||
}
|
||||
});
|
||||
|
||||
marker.addTo(map);
|
||||
|
||||
return marker;
|
||||
}
|
||||
74
support/laravel-maps/resources/js/utils/parser.js
vendored
Normal file
74
support/laravel-maps/resources/js/utils/parser.js
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
import {logError} from './helper';
|
||||
|
||||
const parseMap = element => JSON.parse(
|
||||
element.dataset.mapGoogle
|
||||
|| element.dataset.mapOsm
|
||||
|| element.dataset.mapBing
|
||||
|| element.dataset.mapMapquest
|
||||
|| element.dataset.mapYandex
|
||||
|| element.dataset.mapMapkit
|
||||
);
|
||||
|
||||
const parseService = element => {
|
||||
const {key, type} = JSON.parse(element.dataset.mapService);
|
||||
|
||||
return {
|
||||
key,
|
||||
type,
|
||||
}
|
||||
};
|
||||
|
||||
const parseMarkers = element => {
|
||||
const markers = JSON.parse(element.dataset.mapMarkers) || [];
|
||||
return markers.map(marker => {
|
||||
const lat = parseNumberFloat(marker.lat);
|
||||
const lng = parseNumberFloat(marker.lng);
|
||||
|
||||
const {title, url, popup, icon, icon_size, icon_anchor} = marker;
|
||||
|
||||
return {
|
||||
title,
|
||||
lat,
|
||||
lng,
|
||||
url,
|
||||
popup,
|
||||
icon,
|
||||
iconSize: icon_size || marker.iconSize,
|
||||
iconAnchor: icon_anchor || marker.iconAnchor,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const parseNumberFloat = number => {
|
||||
return typeof number === 'string'
|
||||
? parseFloat(number)
|
||||
: number;
|
||||
};
|
||||
|
||||
const parseNumberInt = number => {
|
||||
return typeof number === 'string'
|
||||
? parseFloat(number)
|
||||
: number;
|
||||
};
|
||||
|
||||
export default {
|
||||
map(element) {
|
||||
try {
|
||||
const map = parseMap(element);
|
||||
const lat = parseNumberFloat(map.lat);
|
||||
const lng = parseNumberFloat(map.lng);
|
||||
const zoom = parseNumberInt(map.zoom);
|
||||
const service = parseService(element);
|
||||
const markers = parseMarkers(element);
|
||||
return {
|
||||
lat,
|
||||
lng,
|
||||
zoom,
|
||||
service,
|
||||
markers,
|
||||
};
|
||||
} catch (e) {
|
||||
logError(e);
|
||||
}
|
||||
},
|
||||
}
|
||||
30
support/laravel-maps/resources/sass/index.scss
vendored
Normal file
30
support/laravel-maps/resources/sass/index.scss
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
.gnw-map-service {
|
||||
position: relative;
|
||||
height: 400px;
|
||||
|
||||
&__google {
|
||||
background: rgb(229, 227, 223);
|
||||
}
|
||||
&__osm,
|
||||
&__bing,
|
||||
&__mapquest {
|
||||
background: rgb(221, 221, 221);
|
||||
}
|
||||
&__yandex {
|
||||
background: rgb(243, 241, 237);
|
||||
}
|
||||
&__mapkit {
|
||||
background: rgb(248, 244, 236);
|
||||
}
|
||||
}
|
||||
|
||||
.gnw-map {
|
||||
height: inherit;
|
||||
|
||||
// Fix Mapkit canvas
|
||||
.mk-map-view {
|
||||
> .syrup-canvas {
|
||||
margin-left: -50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
support/laravel-maps/resources/views/index.blade.php
Normal file
23
support/laravel-maps/resources/views/index.blade.php
Normal file
@@ -0,0 +1,23 @@
|
||||
@if ($enabled)
|
||||
<div class="gnw-map-service gnw-map-service__{{ $service }}">
|
||||
<div class="gnw-map fade" data-map-{{ $service }}="{{ json_encode(compact('lat', 'lng', 'zoom')) }}" data-map-service="{{ json_encode(config('vendor.maps.services.'.$service)) }}" data-map-markers="{{ json_encode($markers ?? []) }}"></div>
|
||||
</div>
|
||||
{{--
|
||||
<div class="col-lg-12"></div>
|
||||
<div class="clear-fix"></div>
|
||||
|
||||
<div class="col-lg-12"></div>
|
||||
<div class="clear-fix"></div>
|
||||
|
||||
<iframe class="map" width="800" height="450" frameborder="0" style="border:0" src="https://www.google.com/maps/embed/v1/place?key={{ $key ?? '' }}&q={{ $lat }},{{ $lng }}¢er={{ $lat }},{{ $lng }}&zoom={{ $zoom }}" scrolling="no">
|
||||
</iframe>
|
||||
|
||||
<div class="col-lg-12"></div>
|
||||
<div class="clear-fix"></div>
|
||||
|
||||
<div class="map-container fade in">
|
||||
<iframe class="map" width="800" height="800" frameborder="0" style="border:0" src="https://www.bing.com/maps/embed?h=800&w=800&cp={{ $lat }}~{{ $lng }}&lvl={{ $zoom }}&typ=d&sty=r&src=SHELL&FORM=MBEDV8" scrolling="no">
|
||||
</iframe>
|
||||
</div>
|
||||
--}}
|
||||
@endif
|
||||
24
support/laravel-maps/resources/views/scripts.blade.php
Normal file
24
support/laravel-maps/resources/views/scripts.blade.php
Normal file
@@ -0,0 +1,24 @@
|
||||
@if ($enabled)
|
||||
{{--TODO: If overriding service via @map() then service is not working--}}
|
||||
@if ($service == 'osm' || $service == 'bing' || $service == 'mapquest')
|
||||
<script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js" integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==" crossorigin="" type="text/javascript"></script>
|
||||
{{-- TODO check if bing needs polyfill: https://github.com/digidem/leaflet-bing-layer--}}
|
||||
@endif
|
||||
<script src="{{ asset(mix('js/index.js', 'vendor/maps')) }}" type="text/javascript"></script>
|
||||
@if ($service == 'mapkit')
|
||||
<script src="https://cdn.apple-mapkit.com/mk/5.x.x/mapkit.js" type="text/javascript"></script>
|
||||
@endif
|
||||
@if ($service == 'mapquest')
|
||||
<script src="https://api.mqcdn.com/sdk/mapquest-js/v1.3.2/mapquest-core.js" type="text/javascript"></script>
|
||||
@endif
|
||||
@if ($service == 'yandex')
|
||||
@if (!empty($key = config('vendor.maps.services.yandex.key')))
|
||||
<script src="https://enterprise.api-maps.yandex.ru/2.1/?lang=en_US&apikey={{ $key }}&onload=onYandexMapsReady" type="text/javascript" async defer></script>
|
||||
@else
|
||||
<script src="https://api-maps.yandex.ru/2.1/?lang=en_US&onload=onYandexMapsReady" type="text/javascript" async defer></script>
|
||||
@endif
|
||||
@endif
|
||||
@if ($service == 'google')
|
||||
<script src="https://maps-api-ssl.google.com/maps/api/js?v=3&ie=UTF8&oe=UTF8&key={{ config('vendor.maps.services.google.key') }}&language={{ app()->getLocale() }}&callback=onGoogleMapsReady" type="text/javascript" async defer></script>
|
||||
@endif
|
||||
@endif
|
||||
9
support/laravel-maps/resources/views/styles.blade.php
Normal file
9
support/laravel-maps/resources/views/styles.blade.php
Normal file
@@ -0,0 +1,9 @@
|
||||
@if ($enabled)
|
||||
@if ($service == 'osm' || $service == 'bing' || $service == 'mapquest')
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin="" type="text/css">
|
||||
@endif
|
||||
@if ($service == 'mapquest')
|
||||
<link rel="stylesheet" href="https://api.mqcdn.com/sdk/mapquest-js/v1.3.2/mapquest-core.css" type="text/css">
|
||||
@endif
|
||||
<link rel="stylesheet" href="{{ asset(mix('css/index.css', 'vendor/maps')) }}" type="text/css">
|
||||
@endif
|
||||
83
support/laravel-maps/src/MapsServiceProvider.php
Normal file
83
support/laravel-maps/src/MapsServiceProvider.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace GoNoWare\Maps;
|
||||
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\ServiceProvider as BaseServiceProvider;
|
||||
|
||||
/*
|
||||
TODO:
|
||||
https://www.bing.com/maps/embed-a-map
|
||||
https://developers.google.com/maps/documentation/maps-static/intro
|
||||
https://wiki.openstreetmap.org/wiki/OpenLinkMap#Embed_map_in_another_website
|
||||
|
||||
embed via iframe:
|
||||
- bing
|
||||
- osm?
|
||||
javascript api:
|
||||
- all
|
||||
|
||||
Add custom marker
|
||||
|
||||
Add custom popup
|
||||
*/
|
||||
|
||||
class MapsServiceProvider extends BaseServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->publishFiles();
|
||||
|
||||
Blade::include('maps::styles', 'mapstyles');
|
||||
Blade::include('maps::scripts', 'mapscripts');
|
||||
Blade::include('maps::index', 'map');
|
||||
|
||||
$this->loadViewsFrom(__DIR__.'/../resources/views', 'maps');
|
||||
|
||||
view()->composer('maps::*', function ($view) {
|
||||
if (!isset($view->service)) {
|
||||
$view->with('service', config('vendor.maps.default'));
|
||||
}
|
||||
if (!isset($view->enabled)) {
|
||||
$view->with('enabled', config('vendor.maps.enabled'));
|
||||
}
|
||||
|
||||
return $view;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->mergeConfigFrom(__DIR__.'/../config/maps.php', 'vendor.maps');
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish files.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function publishFiles()
|
||||
{
|
||||
if ($this->app->runningInConsole()) {
|
||||
$this->publishes([
|
||||
__DIR__.'/../config/maps.php' => config_path('vendor/maps.php'),
|
||||
], 'config');
|
||||
$this->publishes([
|
||||
__DIR__.'/../public' => public_path('vendor/maps'),
|
||||
], 'public');
|
||||
$this->publishes([
|
||||
__DIR__.'/../public' => public_path('vendor/maps'),
|
||||
], 'maps');
|
||||
}
|
||||
}
|
||||
}
|
||||
16
support/laravel-maps/styleci.yml
Normal file
16
support/laravel-maps/styleci.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
preset: laravel
|
||||
|
||||
linting: true
|
||||
|
||||
enabled:
|
||||
- alpha_ordered_imports
|
||||
|
||||
disabled:
|
||||
- length_ordered_imports
|
||||
|
||||
finder:
|
||||
exclude:
|
||||
- "node_modules"
|
||||
- "public"
|
||||
- "tests"
|
||||
- "vendor"
|
||||
0
support/laravel-maps/tests/.gitkeep
Normal file
0
support/laravel-maps/tests/.gitkeep
Normal file
7
support/laravel-maps/webpack.mix.js
vendored
Normal file
7
support/laravel-maps/webpack.mix.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
const mix = require('laravel-mix');
|
||||
|
||||
mix.setPublicPath('public')
|
||||
.js('resources/js/index.js', 'js')
|
||||
.sass('resources/sass/index.scss', 'css')
|
||||
.copy('resources/img/*.png', 'img')
|
||||
.version('img');
|
||||
5611
support/laravel-maps/yarn.lock
Normal file
5611
support/laravel-maps/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
35
support/laravel-translation/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
35
support/laravel-translation/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
17
support/laravel-translation/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
17
support/laravel-translation/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
24
support/laravel-translation/.github/workflows/run-tests.yml
vendored
Normal file
24
support/laravel-translation/.github/workflows/run-tests.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.0
|
||||
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite
|
||||
coverage: none
|
||||
|
||||
- name: Install Composer dependencies
|
||||
run: composer install --prefer-dist --no-interaction
|
||||
|
||||
- name: Execute tests
|
||||
run: vendor/bin/phpunit
|
||||
3
support/laravel-translation/.gitignore
vendored
Normal file
3
support/laravel-translation/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/vendor/
|
||||
/node_modules
|
||||
.phpunit.result.cache
|
||||
4
support/laravel-translation/.styleci.yml
Normal file
4
support/laravel-translation/.styleci.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
preset: laravel
|
||||
finder:
|
||||
exclude:
|
||||
- "tests/fixtures"
|
||||
21
support/laravel-translation/LICENSE.md
Normal file
21
support/laravel-translation/LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Joe Dixon
|
||||
|
||||
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.
|
||||
44
support/laravel-translation/composer.json
Normal file
44
support/laravel-translation/composer.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "joedixon/laravel-translation",
|
||||
"description": "A tool for managing all of your Laravel translations",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Joe Dixon",
|
||||
"email": "hello@joedixon.co.uk"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"illuminate/support": "^8.0||^9.0||^10.0",
|
||||
"laravel/legacy-factories": "^1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"orchestra/testbench": "^6.0",
|
||||
"phpunit/phpunit": "^9.0",
|
||||
"mockery/mockery": "^1.0.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"JoeDixon\\Translation\\": "src"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"JoeDixon\\Translation\\Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vendor/bin/phpunit",
|
||||
"test-coverage": "vendor/bin/phpunit --coverage-html coverage"
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"JoeDixon\\Translation\\TranslationServiceProvider",
|
||||
"JoeDixon\\Translation\\TranslationBindingsServiceProvider"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
7808
support/laravel-translation/composer.lock
generated
Normal file
7808
support/laravel-translation/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
77
support/laravel-translation/config/translation.php
Normal file
77
support/laravel-translation/config/translation.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Package driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The package supports different drivers for translation management.
|
||||
|
|
||||
| Supported: "file", "database"
|
||||
|
|
||||
*/
|
||||
'driver' => 'file',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Route group configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The package ships with routes to handle language management. Update the
|
||||
| configuration here to configure the routes with your preferred group options.
|
||||
|
|
||||
*/
|
||||
'route_group_config' => [
|
||||
'middleware' => 'web',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Translation methods
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Update this array to tell the package which methods it should look for
|
||||
| when finding missing translations.
|
||||
|
|
||||
*/
|
||||
'translation_methods' => ['trans', '__'],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Scan paths
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Update this array to tell the package which directories to scan when
|
||||
| looking for missing translations.
|
||||
|
|
||||
*/
|
||||
'scan_paths' => [app_path(), resource_path()],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| UI URL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Define the URL used to access the language management too.
|
||||
|
|
||||
*/
|
||||
'ui_url' => 'languages',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Database settings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Define the settings for the database driver here.
|
||||
|
|
||||
*/
|
||||
'database' => [
|
||||
|
||||
'connection' => '',
|
||||
|
||||
'languages_table' => 'languages',
|
||||
|
||||
'translations_table' => 'translations',
|
||||
],
|
||||
];
|
||||
15
support/laravel-translation/contributing.md
Normal file
15
support/laravel-translation/contributing.md
Normal file
@@ -0,0 +1,15 @@
|
||||
CONTRIBUTING
|
||||
============
|
||||
|
||||
Contributions are welcome, and are accepted via pull requests. Please review these guidelines before submitting any pull requests.
|
||||
|
||||
## Guidelines
|
||||
|
||||
* Please follow the [PSR-2 Coding Style Guide](http://www.php-fig.org/psr/psr-2/), enforced by [StyleCI](https://styleci.io/).
|
||||
* Ensure that the current tests pass, and if you've added something new, add the tests where relevant.
|
||||
* Send a coherent commit history, making sure each individual commit in your pull request is meaningful.
|
||||
* You may need to [rebase](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) to avoid merge conflicts.
|
||||
* If you are changing the behavior, or the public api, you may need to update the docs.
|
||||
* Please remember that we follow [SemVer](http://semver.org/).
|
||||
|
||||
We have [StyleCI](https://styleci.io/) setup to automatically fix any code style issues.
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
use Faker\Generator;
|
||||
use JoeDixon\Translation\Language;
|
||||
|
||||
$factory->define(Language::class, function (Generator $faker) {
|
||||
return [
|
||||
'language' => $faker->word,
|
||||
'name' => $faker->word,
|
||||
];
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Faker\Generator;
|
||||
use JoeDixon\Translation\Language;
|
||||
use JoeDixon\Translation\Translation;
|
||||
|
||||
$factory->define(Translation::class, function (Generator $faker) {
|
||||
return [
|
||||
'language_id' => function () {
|
||||
return factory(Language::class)->create()->id;
|
||||
},
|
||||
'group' => $faker->word,
|
||||
'key' => $faker->word,
|
||||
'value' => $faker->sentence,
|
||||
];
|
||||
});
|
||||
|
||||
$factory->state(Translation::class, 'group', function (Generator $faker) {
|
||||
return [
|
||||
'language_id' => function () {
|
||||
return factory(Language::class)->create()->id;
|
||||
},
|
||||
'group' => $faker->word,
|
||||
'key' => $faker->word,
|
||||
'value' => $faker->sentence,
|
||||
];
|
||||
});
|
||||
|
||||
$factory->state(Translation::class, 'single', function (Generator $faker) {
|
||||
return [
|
||||
'language_id' => function () {
|
||||
return factory(Language::class)->create()->id;
|
||||
},
|
||||
'group' => 'single',
|
||||
'key' => $faker->word,
|
||||
'value' => $faker->sentence,
|
||||
];
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use JoeDixon\Translation\Language;
|
||||
|
||||
class CreateLanguagesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::connection(config('translation.database.connection'))
|
||||
->create(config('translation.database.languages_table'), function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('name')->nullable();
|
||||
$table->string('language');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
$initialLanguages = array_unique([
|
||||
config('app.fallback_locale'),
|
||||
config('app.locale'),
|
||||
]);
|
||||
|
||||
foreach ($initialLanguages as $language) {
|
||||
Language::firstOrCreate([
|
||||
'language' => $language,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::connection(config('translation.database.connection'))
|
||||
->dropIfExists(config('translation.database.languages_table'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateTranslationsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::connection(config('translation.database.connection'))
|
||||
->create(config('translation.database.translations_table'), function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->unsignedInteger('language_id');
|
||||
$table->foreign('language_id')->references('id')
|
||||
->on(config('translation.database.languages_table'));
|
||||
$table->string('group')->nullable();
|
||||
$table->text('key');
|
||||
$table->text('value')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::connection(config('translation.database.connection'))
|
||||
->dropIfExists(config('translation.database.translations_table'));
|
||||
}
|
||||
}
|
||||
BIN
support/laravel-translation/logo.png
Normal file
BIN
support/laravel-translation/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
4
support/laravel-translation/mix-manifest.json
Normal file
4
support/laravel-translation/mix-manifest.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"/Users/joe/Code/translation/public/vendor/translation/js/app.js": "/Users/joe/Code/translation/public/vendor/translation/js/app.js",
|
||||
"/../../../public/vendor/translation/css/main.css": "/../../../public/vendor/translation/css/main.css"
|
||||
}
|
||||
21940
support/laravel-translation/package-lock.json
generated
Normal file
21940
support/laravel-translation/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
19
support/laravel-translation/package.json
Normal file
19
support/laravel-translation/package.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"watch": "NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"hot": "NODE_ENV=development webpack-dev-server --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"production": "NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"laravel-mix": "^4.1.2",
|
||||
"postcss": "^7.0.36",
|
||||
"tailwindcss": "^0.6.6",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.18.1",
|
||||
"vue": "^2.5.21"
|
||||
}
|
||||
}
|
||||
19
support/laravel-translation/phpunit.xml
Normal file
19
support/laravel-translation/phpunit.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" backupStaticAttributes="false" bootstrap="vendor/autoload.php" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">./src</directory>
|
||||
</include>
|
||||
<exclude>
|
||||
<directory>./src/Console</directory>
|
||||
</exclude>
|
||||
</coverage>
|
||||
<testsuites>
|
||||
<testsuite name="Feature">
|
||||
<directory suffix="Test.php">./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<php>
|
||||
<env name="APP_KEY" value="AckfSECXIvnK5r28GVIWUAxmbBSjTsmF"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
1
support/laravel-translation/public/assets/css/main.css
Normal file
1
support/laravel-translation/public/assets/css/main.css
Normal file
File diff suppressed because one or more lines are too long
1
support/laravel-translation/public/assets/js/app.js
Normal file
1
support/laravel-translation/public/assets/js/app.js
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"/js/app.js": "/js/app.js",
|
||||
"/css/main.css": "/css/main.css"
|
||||
}
|
||||
201
support/laravel-translation/readme.md
Normal file
201
support/laravel-translation/readme.md
Normal file
@@ -0,0 +1,201 @@
|
||||
<div align="center">
|
||||
|
||||

|
||||
|
||||
Translation management for your Laravel application.
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
</div>
|
||||
|
||||
------
|
||||
|
||||
## About Laravel Translation
|
||||
|
||||
Laravel Translation is a package for Laravel which allows you full control
|
||||
over your translations when using [Laravel's
|
||||
localization](https://laravel.com/docs/5.7/localization) functionality.
|
||||
|
||||
The package allows you to manage your translations using either the native file
|
||||
based translations, but also provides a database driver which is useful in
|
||||
multi-server setups.
|
||||
|
||||
It exposes a user interface allowing you to update existing and add new
|
||||
translations to your application.
|
||||
|
||||
Below are a full list of features:
|
||||
|
||||
- File and database drivers
|
||||
- Database translation loader (automatically load translations from the database
|
||||
when Laravel's translation retrieval methods and the database driver)
|
||||
- User interface to add new languages and add and update translations
|
||||
- Artisan commands to manage your translations
|
||||
- Scan your application for missing translations
|
||||
|
||||
## Version Compatibility
|
||||
|
||||
| Laravel | Laravel Translation |
|
||||
| ------------- | ------------------- |
|
||||
| 6.x | 1.x |
|
||||
| 7.x | 1.x |
|
||||
| 8.x | 2.x |
|
||||
| 9.x | 2.x |
|
||||
|
||||
## Installation
|
||||
|
||||
Install the package via Composer
|
||||
|
||||
`composer require joedixon/laravel-translation`
|
||||
|
||||
Publish configuration and assets
|
||||
|
||||
`php artisan vendor:publish --provider="JoeDixon\Translation\TranslationServiceProvider"`
|
||||
|
||||
The service provider is loaded automatically using [package discovery](https://laravel.com/docs/5.7/packages#package-discovery).
|
||||
|
||||
## Usage
|
||||
|
||||
### Configuration
|
||||
|
||||
The package ships with a configuration file called `translation.php` which is published to the
|
||||
config directory during installation. Below is an outline of the settings.
|
||||
|
||||
```
|
||||
driver [file|database]
|
||||
```
|
||||
Choose either `file` or `database`. File translations utilise Laravel's native
|
||||
file based translations and includes support for both `array` based and `json` based
|
||||
language files.
|
||||
|
||||
```
|
||||
route_group_config.middleware [string|array]
|
||||
```
|
||||
Apply middleware to the routes which ship with the package. For example, you may
|
||||
which to use the `auth` middleware to ensure package user interface is only
|
||||
accessible to logged in users.
|
||||
|
||||
```
|
||||
translation_methods [array]
|
||||
```
|
||||
Choose which of Laravel's translation methods to use when searching for missing
|
||||
translation keys.
|
||||
|
||||
```
|
||||
scan_paths [array]
|
||||
```
|
||||
Choose which paths to use when searching for missing translations. Narrowing the
|
||||
search to specific directories will result in a performance increase when
|
||||
scanning for missing translations.
|
||||
|
||||
```
|
||||
ui_url [string]
|
||||
```
|
||||
Choose the root URL where the package user interface can be accessed. All routes
|
||||
will be prefixed by this value.
|
||||
|
||||
e.g. setting this value to `languages` will result in URLs such as `translations/{language}/translations`
|
||||
|
||||
```
|
||||
database.languages_table
|
||||
```
|
||||
Choose the name of the languages table when using the database driver.
|
||||
|
||||
```
|
||||
database.translations_table
|
||||
```
|
||||
Choose the name of the translations table when using the database driver.
|
||||
|
||||
### Drivers
|
||||
|
||||
#### File
|
||||
Utitlises Laravel's native php array and JSON based language files and exposes a
|
||||
user interface to manage the enclosed translations. Add and update languages and translations
|
||||
using either the user interface or the built-in [Artisan commands](https://laravel.com/docs/5.7/artisan).
|
||||
|
||||
#### Database
|
||||
The database driver takes all of the functionality of Laravel's file based
|
||||
language files, but moves the storage to the database, utilising the connection
|
||||
configured for your Laravel application.
|
||||
|
||||
It also replaces the translation loader in the container so all of Laravel's
|
||||
translation retrieval methods (`__()`, `trans()`, `@lang()`, etc) will load the
|
||||
relevant strings from the database rather than the files without the need to
|
||||
change any code in your application. It's a like for like swap.
|
||||
|
||||
To utilise the database driver, make sure to update the database table names in
|
||||
the configuration file and run the migrations.
|
||||
|
||||
#### Changing Drivers from File (default) to Database
|
||||
|
||||
1. Update the driver to use database in `./config/translation.php`.
|
||||
|
||||
```php
|
||||
'driver' => 'database'
|
||||
```
|
||||
|
||||
2. Run the migration to add translations and languages tables.
|
||||
|
||||
```shell
|
||||
php artisan migrate
|
||||
```
|
||||
|
||||
3. Run the following command and folow the prompts to synchronise the translations between drivers.
|
||||
|
||||
```shell
|
||||
php artisan translation:sync-translations
|
||||
```
|
||||
|
||||
4. A few questions will be prompted which have to be answered. See the screenshot below:
|
||||
|
||||
### User interface
|
||||
Navigate to http://your-project.test/languages (update `languages` to match the
|
||||
`translation.ui_url` configuration setting) and use the interface to manage
|
||||
your translations.
|
||||
|
||||
First, click on the language you wish to edit. On the subsequent page, find the
|
||||
translation you want to edit and click on the pencil icon or on the text and
|
||||
make your edits. As soon as you remove focus from the input, your translation
|
||||
will be saved, indicated by the green check icon.
|
||||
|
||||
### Artisan Commands
|
||||
The package ships with a series of Artisan commands which assist with
|
||||
translation management.
|
||||
|
||||
```
|
||||
translation:add-language
|
||||
```
|
||||
Add a new language to the application.
|
||||
|
||||
```
|
||||
translation:add-translation-key
|
||||
```
|
||||
Add a new language key for the application.
|
||||
|
||||
```
|
||||
translation:list-languages
|
||||
```
|
||||
List all of the available languages in the application.
|
||||
|
||||
```
|
||||
translation:list-missing-translation-keys
|
||||
```
|
||||
List all of the translation keys in the app which don't have a corresponding translation.
|
||||
|
||||
```
|
||||
translation:sync-translations
|
||||
```
|
||||
Synchronise translations between drivers. This is useful if you have an exisitng
|
||||
application using the native file based language files and wish to move to the
|
||||
database driver. Running this command will take all of the translations from the
|
||||
language files and insert them in to the database.
|
||||
|
||||
```
|
||||
translation:sync-missing-translation-keys
|
||||
```
|
||||
This command will scan your project (using the paths supplied in the
|
||||
configuration file) and create all of the missing translation keys. This can be
|
||||
run for all languages or a single language.
|
||||
|
||||
262
support/laravel-translation/resources/assets/css/main.css
Normal file
262
support/laravel-translation/resources/assets/css/main.css
Normal file
@@ -0,0 +1,262 @@
|
||||
/**
|
||||
* This injects Tailwind's base styles, which is a combination of
|
||||
* Normalize.css and some additional base styles.
|
||||
*
|
||||
* You can see the styles here:
|
||||
* https://github.com/tailwindcss/tailwindcss/blob/master/css/preflight.css
|
||||
*
|
||||
* If using `postcss-import`, use this import instead:
|
||||
*
|
||||
* @import "tailwindcss/preflight";
|
||||
*/
|
||||
|
||||
@tailwind preflight;
|
||||
/**
|
||||
* This injects any component classes registered by plugins.
|
||||
*
|
||||
* If using `postcss-import`, use this import instead:
|
||||
*
|
||||
* @import "tailwindcss/components";
|
||||
*/
|
||||
|
||||
@tailwind components;
|
||||
/**
|
||||
* Here you would add any of your custom component classes; stuff that you'd
|
||||
* want loaded *before* the utilities so that the utilities could still
|
||||
* override them.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* .btn { ... }
|
||||
* .form-input { ... }
|
||||
*
|
||||
* Or if using a preprocessor or `postcss-import`:
|
||||
*
|
||||
* @import "components/buttons";
|
||||
* @import "components/forms";
|
||||
*/
|
||||
|
||||
/**
|
||||
* This injects all of Tailwind's utility classes, generated based on your
|
||||
* config file.
|
||||
*
|
||||
* If using `postcss-import`, use this import instead:
|
||||
*
|
||||
* @import "tailwindcss/utilities";
|
||||
*/
|
||||
|
||||
@tailwind utilities;
|
||||
/**
|
||||
* Here you would add any custom utilities you need that don't come out of the
|
||||
* box with Tailwind.
|
||||
*
|
||||
* Example :
|
||||
*
|
||||
* .bg-pattern-graph-paper { ... }
|
||||
* .skew-45 { ... }
|
||||
*
|
||||
* Or if using a preprocessor or `postcss-import`:
|
||||
*
|
||||
* @import "utilities/background-patterns";
|
||||
* @import "utilities/skew-transforms";
|
||||
*/
|
||||
|
||||
body {
|
||||
@apply bg-grey-lighter text-grey-darkest
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
@apply flex;
|
||||
}
|
||||
|
||||
li {
|
||||
@apply px-4;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-blue;
|
||||
}
|
||||
|
||||
nav.header {
|
||||
background: linear-gradient(90deg, #125b93, #2891c4);
|
||||
@apply border-b flex items-center h-16 text-white w-full
|
||||
}
|
||||
|
||||
nav a {
|
||||
@apply .opacity-75 text-white no-underline flex items-center
|
||||
}
|
||||
|
||||
nav a.active {
|
||||
@apply opacity-100
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
@apply opacity-100 underline
|
||||
}
|
||||
|
||||
.panel {
|
||||
@apply bg-white rounded m-6 shadow text-grey-dark
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
@apply p-4 text-lg border-b flex items-center font-thin
|
||||
}
|
||||
|
||||
.panel-footer {
|
||||
@apply border-t bg-grey-lighter p-4
|
||||
}
|
||||
|
||||
.panel-body table {
|
||||
@apply w-full table-fixed;
|
||||
}
|
||||
|
||||
.panel-body th,
|
||||
.panel-body td {
|
||||
@apply text-left p-4 overflow-x-auto
|
||||
}
|
||||
|
||||
.panel-body th {
|
||||
@apply text-grey-darker
|
||||
}
|
||||
|
||||
.panel-body td {
|
||||
@apply font-thin align-top
|
||||
}
|
||||
|
||||
.panel-body tr {
|
||||
@apply border-b
|
||||
}
|
||||
|
||||
.panel-body thead tr {
|
||||
@apply bg-grey-lighter
|
||||
}
|
||||
|
||||
.panel-body tbody tr:nth-child(even) {
|
||||
@apply bg-grey-lighter
|
||||
}
|
||||
|
||||
.panel-body tbody tr:hover,
|
||||
.panel-body tbody tr:nth-child(even):hover {
|
||||
@apply bg-blue-lightest
|
||||
}
|
||||
|
||||
.panel-body tbody tr:last-child {
|
||||
@apply border-none
|
||||
}
|
||||
|
||||
.panel-body td textarea {
|
||||
overflow-wrap: inherit;
|
||||
@apply border-none resize-none bg-transparent text-grey-darker w-full font-thin h-auto p-0
|
||||
}
|
||||
|
||||
.panel-body td textarea.active {
|
||||
@apply w-full rounded h-32 p-2 border border-solid border-grey
|
||||
}
|
||||
|
||||
.panel-body td textarea:focus {
|
||||
@apply outline-none;
|
||||
}
|
||||
|
||||
.button {
|
||||
@apply bg-transparent text-grey-darker py-2 px-4 border border-grey rounded text-sm font-bold no-underline
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
@apply text-blue
|
||||
}
|
||||
|
||||
.button-blue {
|
||||
@apply bg-blue text-white border-blue
|
||||
}
|
||||
|
||||
.button-blue:hover {
|
||||
@apply text-white bg-blue-dark
|
||||
}
|
||||
|
||||
.input-group {
|
||||
@apply w-full mb-6
|
||||
}
|
||||
|
||||
.input-group label {
|
||||
@apply block uppercase tracking-wide text-grey-darker text-xs font-bold mb-2
|
||||
}
|
||||
|
||||
.input-group input {
|
||||
@apply appearance-none block w-full bg-grey-lighter text-grey-darker border rounded py-3 px-4 mb-3 leading-tight
|
||||
}
|
||||
|
||||
.input-group:last-child {
|
||||
@apply mb-0
|
||||
}
|
||||
|
||||
.input-group input.error {
|
||||
@apply border-red
|
||||
}
|
||||
|
||||
.input-group .error-text {
|
||||
@apply text-red text-xs italic
|
||||
}
|
||||
|
||||
.select-group {
|
||||
@apply relative mr-2
|
||||
}
|
||||
|
||||
.select-group:last-child {
|
||||
@apply m-0
|
||||
}
|
||||
|
||||
.select-group select {
|
||||
@apply text-base block appearance-none bg-white border text-grey-darker uppercase py-2 px-4 pr-8 rounded leading-tight max-w-xs font-thin
|
||||
}
|
||||
|
||||
.select-group select:focus {
|
||||
@apply outline-none border-grey
|
||||
}
|
||||
|
||||
.select-group .caret {
|
||||
@apply pointer-events-none absolute pin-y pin-r flex items-center px-2 text-grey-darker
|
||||
}
|
||||
|
||||
.select-group .caret svg {
|
||||
@apply fill-current h-4 w-4
|
||||
}
|
||||
|
||||
.w-1\/10 {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
background: url('data:image/svg+xml;charset=utf8,<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" xmlns:bx="https://boxy-svg.com"><path d="M 11.732 12.439 C 9.047 14.525 5.109 12.921 4.645 9.553 C 4.181 6.183 7.538 3.575 10.689 4.858 C 13.399 5.962 14.306 9.349 12.512 11.66 L 15.466 14.603 L 14.682 15.388 L 11.738 12.439 Z M 9.027 12.263 C 11.577 12.263 13.172 9.5 11.897 7.291 C 11.304 6.267 10.21 5.634 9.027 5.634 C 6.476 5.634 4.88 8.397 6.157 10.606 C 6.749 11.63 7.842 12.263 9.027 12.263 Z" style="fill: rgb(96, 111, 123);"/></svg>');
|
||||
@apply bg-grey-lighter rounded pl-10 py-2 pr-4 bg-no-repeat bg-contain transition border text-grey-darker font-thin w-full
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
@apply outline-none bg-white border border-grey-light
|
||||
}
|
||||
|
||||
.transition {
|
||||
transition: all .1s ease-in;
|
||||
}
|
||||
|
||||
.search {
|
||||
max-width: 500px;
|
||||
@apply mx-2 relative flex-1
|
||||
}
|
||||
|
||||
ul.search-results {
|
||||
max-height: 300px;
|
||||
@apply font-thin pl-0 block absolute w-full bg-grey-lighter border border-t-0 rounded rounded-t-none overflow-x-hidden overflow-y-scroll
|
||||
}
|
||||
|
||||
ul.search-results li {
|
||||
@apply px-4 py-2 border-b pl-10;
|
||||
}
|
||||
|
||||
ul.search-results li:last-child {
|
||||
@apply border-b-0;
|
||||
}
|
||||
|
||||
.search.has-results .search-input {
|
||||
@apply border-b-0 rounded-b-none bg-white
|
||||
}
|
||||
63
support/laravel-translation/resources/assets/js/app.js
Normal file
63
support/laravel-translation/resources/assets/js/app.js
Normal file
@@ -0,0 +1,63 @@
|
||||
|
||||
/**
|
||||
* First we will load all of this project's JavaScript dependencies which
|
||||
* includes Vue and other libraries. It is a great starting point when
|
||||
* building robust, powerful web applications using Vue and Laravel.
|
||||
*/
|
||||
|
||||
// require('./bootstrap');
|
||||
|
||||
/**
|
||||
* We'll load the axios HTTP library which allows us to easily issue requests
|
||||
* to our Laravel back-end. This library automatically handles sending the
|
||||
* CSRF token as a header based on the value of the "XSRF" token cookie.
|
||||
*/
|
||||
|
||||
window.axios = require('axios');
|
||||
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
|
||||
/**
|
||||
* Next we will register the CSRF Token as a common header with Axios so that
|
||||
* all outgoing HTTP requests automatically have it attached. This is just
|
||||
* a simple convenience so we don't have to attach every token manually.
|
||||
*/
|
||||
|
||||
let token = document.head.querySelector('meta[name="csrf-token"]');
|
||||
|
||||
if (token) {
|
||||
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
|
||||
} else {
|
||||
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
|
||||
}
|
||||
|
||||
window.Vue = require('vue');
|
||||
|
||||
/**
|
||||
* Next, we will create a fresh Vue application instance and attach it to
|
||||
* the page. Then, you may begin adding components to this application
|
||||
* or customize the JavaScript scaffolding to fit your unique needs.
|
||||
*/
|
||||
|
||||
Vue.component('translation-input', require('./components/TranslationInput.vue').default);
|
||||
|
||||
const app = new Vue({
|
||||
el: '#app',
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
showAdvancedOptions: false,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
submit: function(event) {
|
||||
event.target.form.submit();
|
||||
},
|
||||
|
||||
toggleAdvancedOptions(event) {
|
||||
event.preventDefault();
|
||||
this.showAdvancedOptions = !this.showAdvancedOptions;
|
||||
}
|
||||
}
|
||||
});
|
||||
56
support/laravel-translation/resources/assets/js/bootstrap.js
vendored
Normal file
56
support/laravel-translation/resources/assets/js/bootstrap.js
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
window._ = require('lodash');
|
||||
window.Popper = require('popper.js').default;
|
||||
|
||||
/**
|
||||
* We'll load jQuery and the Bootstrap jQuery plugin which provides support
|
||||
* for JavaScript based Bootstrap features such as modals and tabs. This
|
||||
* code may be modified to fit the specific needs of your application.
|
||||
*/
|
||||
|
||||
try {
|
||||
window.$ = window.jQuery = require('jquery');
|
||||
|
||||
require('bootstrap');
|
||||
} catch (e) {}
|
||||
|
||||
/**
|
||||
* We'll load the axios HTTP library which allows us to easily issue requests
|
||||
* to our Laravel back-end. This library automatically handles sending the
|
||||
* CSRF token as a header based on the value of the "XSRF" token cookie.
|
||||
*/
|
||||
|
||||
window.axios = require('axios');
|
||||
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
|
||||
/**
|
||||
* Next we will register the CSRF Token as a common header with Axios so that
|
||||
* all outgoing HTTP requests automatically have it attached. This is just
|
||||
* a simple convenience so we don't have to attach every token manually.
|
||||
*/
|
||||
|
||||
let token = document.head.querySelector('meta[name="csrf-token"]');
|
||||
|
||||
if (token) {
|
||||
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
|
||||
} else {
|
||||
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
|
||||
}
|
||||
|
||||
/**
|
||||
* Echo exposes an expressive API for subscribing to channels and listening
|
||||
* for events that are broadcast by Laravel. Echo and event broadcasting
|
||||
* allows your team to easily build robust real-time web applications.
|
||||
*/
|
||||
|
||||
// import Echo from 'laravel-echo'
|
||||
|
||||
// window.Pusher = require('pusher-js');
|
||||
|
||||
// window.Echo = new Echo({
|
||||
// broadcaster: 'pusher',
|
||||
// key: process.env.MIX_PUSHER_APP_KEY,
|
||||
// cluster: process.env.MIX_PUSHER_APP_CLUSTER,
|
||||
// encrypted: true
|
||||
// });
|
||||
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<div class="flex">
|
||||
<svg v-show="!isActive && isLoading" v-on:click="setActive" class="cursor-pointer fill-current w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M0 2C0 .9.9 0 2 0h14l4 4v14a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V2zm5 0v6h10V2H5zm6 1h3v4h-3V3z"/></svg>
|
||||
<svg v-show="!isActive && hasSaved" v-on:click="setActive" class="cursor-pointer fill-current text-green w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm12.73-1.41A8 8 0 1 0 4.34 4.34a8 8 0 0 0 11.32 11.32zM6.7 9.29L9 11.6l4.3-4.3 1.4 1.42L9 14.4l-3.7-3.7 1.4-1.42z"/></svg>
|
||||
<svg v-show="!isActive && hasErrored" v-on:click="setActive" class="cursor-pointer fill-current text-red w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M2.93 17.07A10 10 0 1 1 17.07 2.93 10 10 0 0 1 2.93 17.07zm1.41-1.41A8 8 0 1 0 15.66 4.34 8 8 0 0 0 4.34 15.66zm9.9-8.49L11.41 10l2.83 2.83-1.41 1.41L10 11.41l-2.83 2.83-1.41-1.41L8.59 10 5.76 7.17l1.41-1.41L10 8.59l2.83-2.83 1.41 1.41z"/></svg>
|
||||
<svg v-show="!isActive && !hasSaved && !hasErrored && !isLoading" v-on:click="setActive" class="cursor-pointer fill-current w-4 h-4 mr-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M12.3 3.7l4 4L4 20H0v-4L12.3 3.7zm1.4-1.4L16 0l4 4-2.3 2.3-4-4z"/></svg>
|
||||
<textarea
|
||||
rows="1"
|
||||
v-model="translation"
|
||||
v-bind:class="{ active: isActive }"
|
||||
v-on:focus="setActive"
|
||||
v-on:blur="storeTranslation"
|
||||
ref="trans"
|
||||
></textarea>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['initialTranslation', 'language', 'group', 'translationKey', 'route'],
|
||||
|
||||
data: function() {
|
||||
return {
|
||||
isActive: false,
|
||||
hasSaved: false,
|
||||
hasErrored: false,
|
||||
isLoading: false,
|
||||
hasChanged: false,
|
||||
translation: this.initialTranslation
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
setActive: function() {
|
||||
this.isActive = true;
|
||||
this.$refs.trans.focus();
|
||||
},
|
||||
storeTranslation: function() {
|
||||
this.isActive = false;
|
||||
if(!this.hasChanged) {
|
||||
return;
|
||||
}
|
||||
this.isLoading = true;
|
||||
window.axios.post(`/${this.route}/${this.language}`, {
|
||||
language: this.language,
|
||||
group: this.group,
|
||||
key: this.translationKey,
|
||||
value: this.translation
|
||||
}).then((response) => {
|
||||
this.hasSaved = true;
|
||||
this.isLoading = false;
|
||||
this.hasChanged = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.hasErrored = true;
|
||||
this.isLoading = false;
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
hasSaved: function(hasSaved) {
|
||||
if(hasSaved) {
|
||||
setTimeout(() => { this.hasSaved = false }, 3000)
|
||||
}
|
||||
},
|
||||
hasErrored: function(hasErrored) {
|
||||
if(hasErrored) {
|
||||
setTimeout(() => { this.hasErrored = false }, 3000)
|
||||
}
|
||||
},
|
||||
translation: function(translation)
|
||||
{
|
||||
this.hasChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
113
support/laravel-translation/resources/helpers.php
Normal file
113
support/laravel-translation/resources/helpers.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
if (! function_exists('set_active')) {
|
||||
/**
|
||||
* Determine if a route is the currently active route.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $class
|
||||
* @return string
|
||||
*/
|
||||
function set_active($path, $class = 'active')
|
||||
{
|
||||
return Request::is(config('translation.ui_url').$path) ? $class : '';
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('strs_contain')) {
|
||||
/**
|
||||
* Determine whether any of the provided strings in the haystack contain the needle.
|
||||
*
|
||||
* @param array $haystacks
|
||||
* @param string $needle
|
||||
* @return bool
|
||||
*/
|
||||
function strs_contain($haystacks, $needle)
|
||||
{
|
||||
$haystacks = (array) $haystacks;
|
||||
|
||||
foreach ($haystacks as $haystack) {
|
||||
if (is_array($haystack)) {
|
||||
return strs_contain($haystack, $needle);
|
||||
} elseif (Str::contains(strtolower($haystack), strtolower($needle))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('array_diff_assoc_recursive')) {
|
||||
/**
|
||||
* Recursively diff two arrays.
|
||||
*
|
||||
* @param array $arrayOne
|
||||
* @param array $arrayTwo
|
||||
* @return array
|
||||
*/
|
||||
function array_diff_assoc_recursive($arrayOne, $arrayTwo)
|
||||
{
|
||||
$difference = [];
|
||||
foreach ($arrayOne as $key => $value) {
|
||||
if (is_array($value) || $value instanceof Illuminate\Support\Collection) {
|
||||
if (! isset($arrayTwo[$key])) {
|
||||
$difference[$key] = $value;
|
||||
} elseif (! (is_array($arrayTwo[$key]) || $arrayTwo[$key] instanceof Illuminate\Support\Collection)) {
|
||||
$difference[$key] = $value;
|
||||
} else {
|
||||
$new_diff = array_diff_assoc_recursive($value, $arrayTwo[$key]);
|
||||
if ($new_diff != false) {
|
||||
$difference[$key] = $new_diff;
|
||||
}
|
||||
}
|
||||
} elseif (! isset($arrayTwo[$key])) {
|
||||
$difference[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $difference;
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('str_before')) {
|
||||
/**
|
||||
* Get the portion of a string before a given value.
|
||||
*
|
||||
* @param string $subject
|
||||
* @param string $search
|
||||
* @return string
|
||||
*/
|
||||
function str_before($subject, $search)
|
||||
{
|
||||
return $search === '' ? $subject : explode($search, $subject)[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Array undot
|
||||
if (! function_exists('array_undot')) {
|
||||
/**
|
||||
* Expands a single level array with dot notation into a multi-dimensional array.
|
||||
*
|
||||
* @param array $dotNotationArray
|
||||
* @return array
|
||||
*/
|
||||
function array_undot(array $dotNotationArray)
|
||||
{
|
||||
$array = [];
|
||||
foreach ($dotNotationArray as $key => $value) {
|
||||
// if there is a space after the dot, this could legitimately be
|
||||
// a single key and not nested.
|
||||
if (count(explode('. ', $key)) > 1) {
|
||||
$array[$key] = $value;
|
||||
} else {
|
||||
Arr::set($array, $key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
6
support/laravel-translation/resources/lang/de/errors.php
Normal file
6
support/laravel-translation/resources/lang/de/errors.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'language_exists' => 'Die Sprache { :language } ist bereits vorhanden',
|
||||
'key_exists' => 'Der Übersetzungsschlüssel { :key } ist bereits vorhanden',
|
||||
];
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'languages' => 'Sprachen',
|
||||
'language' => 'Sprache',
|
||||
'type' => 'Typ',
|
||||
'file' => 'Datei',
|
||||
'key' => 'Schlüssel',
|
||||
'prompt_language' => 'Geben Sie den Sprachcode ein, den Sie hinzufügen möchten (z.B. en).',
|
||||
'language_added' => 'Neue Sprache wurde erfolgreich hinzugefügt 🙌',
|
||||
'prompt_language_for_key' => 'Geben Sie die Sprache für den Schlüssel ein (z.B. en)',
|
||||
'prompt_type' => 'Ist das ein Json- oder Array-Schlüssel?',
|
||||
'prompt_file' => 'In welcher Datei wird das gespeichert?',
|
||||
'prompt_key' => 'Was ist der Schlüssel für diese Übersetzung?',
|
||||
'prompt_value' => 'Was ist der Wert für diese Übersetzung',
|
||||
'type_error' => 'Übersetzungstyp muss json oder ein Array sein',
|
||||
'language_key_added' => 'Neuer Sprachenschlüssel wurde erfolgreich hinzugefügt 👏',
|
||||
'no_missing_keys' => 'Es fehlen keine Übersetzungsschlüssel in der App 🎉',
|
||||
'keys_synced' => 'Fehlende Schlüssel erfolgreich synchronisiert 🎊',
|
||||
'search' => 'Alle Übersetzungen suchen',
|
||||
'translations' => 'Übersetzung',
|
||||
'language_name' => 'Name',
|
||||
'locale' => 'locale',
|
||||
'add' => '+ Hinzufügen',
|
||||
'add_language' => 'Neue Sprache hinzufügen',
|
||||
'save' => 'save',
|
||||
'language_exists' => 'Das :attribute ist bereits vorhanden.',
|
||||
'uh_oh' => 'Etwas ist nicht ganz richtig',
|
||||
'group_single' => 'Gruppe / Single',
|
||||
'Gruppe' => 'Gruppe',
|
||||
'single' => 'single',
|
||||
'value' => 'Wert',
|
||||
'namespace' => 'Namespace',
|
||||
'synchronisieren' => 'Übersetzungen synchronisieren ⏳',
|
||||
'synced' => 'Übersetzungen wurden synchronisiert 😎',
|
||||
'add_translation' => 'Übersetzung hinzufügen',
|
||||
'translation_added' => 'Neue Übersetzung erfolgreich hinzugefügt 🙌',
|
||||
'namespace_label' => 'Namespace (optional)',
|
||||
'group_label' => 'Gruppe (optional)',
|
||||
'key_label' => 'Schlüssel',
|
||||
'value_label' => 'Wert',
|
||||
'namespace_placeholder' => 'z.B. my_package',
|
||||
'group_placeholder' => 'z.B. validation',
|
||||
'key_placeholder' => 'z.B. invalid_key',
|
||||
'value_placeholder' => 'z.B. Schlüssel müssen eine einzige Zeichenfolge sein',
|
||||
'advanced_options' => 'Erweiterte Optionen umschalten',
|
||||
];
|
||||
6
support/laravel-translation/resources/lang/en/errors.php
Normal file
6
support/laravel-translation/resources/lang/en/errors.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'language_exists' => 'The language { :language } already exists',
|
||||
'key_exists' => 'The translation key { :key } already exists',
|
||||
];
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'languages' => 'Languages',
|
||||
'language' => 'Language',
|
||||
'type' => 'Type',
|
||||
'file' => 'File',
|
||||
'key' => 'Key',
|
||||
'prompt_language' => 'Enter the language code you would like to add (e.g. en)',
|
||||
'language_added' => 'New language added successfully 🙌',
|
||||
'prompt_language_for_key' => 'Enter the language for the key (e.g. en)',
|
||||
'prompt_type' => 'Is this a json or array key?',
|
||||
'prompt_file' => 'Which file will this be stored in?',
|
||||
'prompt_key' => 'What is the key for this translation?',
|
||||
'prompt_value' => 'What is the value for this translation',
|
||||
'type_error' => 'Translation type must be json or array',
|
||||
'language_key_added' => 'New language key added successfully 👏',
|
||||
'no_missing_keys' => 'There are no missing translation keys in the app 🎉',
|
||||
'keys_synced' => 'Missing keys synchronised successfully 🎊',
|
||||
'search' => 'Search all translations',
|
||||
'translations' => 'Translation',
|
||||
'language_name' => 'Name',
|
||||
'locale' => 'Locale',
|
||||
'add' => '+ Add',
|
||||
'add_language' => 'Add a new language',
|
||||
'save' => 'Save',
|
||||
'language_exists' => 'The :attribute already exists.',
|
||||
'uh_oh' => 'Something\'s not quite right',
|
||||
'group_single' => 'Group / Single',
|
||||
'group' => 'Group',
|
||||
'single' => 'Single',
|
||||
'value' => 'Value',
|
||||
'namespace' => 'Namespace',
|
||||
'add_translation' => 'Add a translation',
|
||||
'translation_added' => 'New translation added successfull 🙌',
|
||||
'namespace_label' => 'Namespace (Optional)',
|
||||
'group_label' => 'Group (Optional)',
|
||||
'key_label' => 'Key',
|
||||
'value_label' => 'Value',
|
||||
'namespace_placeholder' => 'e.g. my_package',
|
||||
'group_placeholder' => 'e.g. validation',
|
||||
'key_placeholder' => 'e.g. invalid_key',
|
||||
'value_placeholder' => 'e.g. Keys must be a single string',
|
||||
'advanced_options' => 'Toggle advanced options',
|
||||
];
|
||||
6
support/laravel-translation/resources/lang/fr/errors.php
Normal file
6
support/laravel-translation/resources/lang/fr/errors.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'key_exists' => 'La clé de traduction { :key } existe déjà',
|
||||
'language_exists' => 'La langue { :language } existe déjà',
|
||||
];
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'add' => '+ Ajouter',
|
||||
'add_language' => 'Ajouter une nouvelle langue',
|
||||
'add_translation' => 'Ajouter une traduction',
|
||||
'advanced_options' => 'Afficher les options avancées',
|
||||
'file' => 'Fichier',
|
||||
'group' => 'Groupe',
|
||||
'group_label' => 'Groupe (Optionnel)',
|
||||
'group_placeholder' => 'Ex: validation',
|
||||
'group_single' => 'Groupe / Unique',
|
||||
'key' => 'Clé',
|
||||
'key_label' => 'Clé',
|
||||
'key_placeholder' => 'Par exemple : invalid_key',
|
||||
'keys_synced' => 'Clés manquantes synchronisées avec succès 🎊',
|
||||
'language' => 'Langue',
|
||||
'language_added' => 'Nouvelle langue ajoutée avec succés 🙌',
|
||||
'language_exists' => 'Le :attribute existe déjà.',
|
||||
'language_key_added' => 'Nouvelle clé dans la langue ajoutée avec succès 👏',
|
||||
'language_name' => 'Nom',
|
||||
'languages' => 'Langues',
|
||||
'locale' => 'Locale',
|
||||
'namespace' => 'Namespace',
|
||||
'namespace_label' => 'Namespace (Optionnel)',
|
||||
'namespace_placeholder' => 'Par exemple : my_package',
|
||||
'no_missing_keys' => 'Il ne manque aucune clé de traduction dans l\'application 🎉',
|
||||
'prompt_file' => 'Dans quel fichier sera t\'elle stockée ?',
|
||||
'prompt_key' => 'Quelle est la clé de cette traduction ?',
|
||||
'prompt_language' => 'Entrez le code langue que vous aimeriez ajouter (Ex: fr)',
|
||||
'prompt_language_for_key' => 'Entrez la langue pour la clé (Ex: fr)',
|
||||
'prompt_type' => 'Est-ce une clé Json ou Array ?',
|
||||
'prompt_value' => 'Quelle est la valeur de la traduction',
|
||||
'save' => 'Sauvegarder',
|
||||
'search' => 'Rechercher toutes les traductions',
|
||||
'single' => 'Unique',
|
||||
'translation_added' => 'Nouvelle traduction ajoutée avec succès 🙌',
|
||||
'translations' => 'Traduction',
|
||||
'type' => 'Type',
|
||||
'type_error' => 'Le type de traduction doit être en json ou en array',
|
||||
'uh_oh' => 'Quelque chose ne fonctionne pas',
|
||||
'value' => 'Valeur',
|
||||
'value_label' => 'Valeur',
|
||||
'value_placeholder' => 'Par exemple : Les clés doivent être une seule chaîne',
|
||||
];
|
||||
6
support/laravel-translation/resources/lang/nl/errors.php
Normal file
6
support/laravel-translation/resources/lang/nl/errors.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'language_exists' => 'De taal { :language } bestaat al',
|
||||
'key_exists' => 'De vertaalsleutel { :key } bestaat al',
|
||||
];
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'languages' => 'Talen',
|
||||
'language' => 'Taal',
|
||||
'type' => 'Type',
|
||||
'file' => 'Bestand',
|
||||
'key' => 'Sleutel',
|
||||
'prompt_language' => 'Voer de taalcode in die u wilt toevoegen (bijvoorbeeld: en)',
|
||||
'language_added' => 'Nieuwe taal met succes toegevoegd 🙌',
|
||||
'prompt_language_for_key' => 'Voer de taal voor de sleutel in (bijvoorbeeld: en)',
|
||||
'prompt_type' => 'Is dit een json- of arraysleutel?',
|
||||
'prompt_file' => 'In welk bestand wordt dit opgeslagen?',
|
||||
'prompt_key' => 'Wat is de sleutel voor deze vertaling?',
|
||||
'prompt_value' => 'Wat is de text voor deze vertaling',
|
||||
'type_error' => 'Het vertaaltype moet json of array zijn',
|
||||
'language_key_added' => 'Nieuwe taalcode toegevoegd 👏',
|
||||
'no_missing_keys' => 'Er zijn geen ontbrekende vertaalsleutels in de app 🎉',
|
||||
'keys_synced' => 'Ontbrekende toetsen gesynchroniseerd met succes 🎊',
|
||||
'search' => 'Doorzoek alle vertalingen',
|
||||
'translations' => 'Vertaling',
|
||||
'language_name' => 'Naam',
|
||||
'locale' => 'locale',
|
||||
'add' => '+ Toevoegen',
|
||||
'add_language' => 'Voeg een nieuwe taal toe',
|
||||
'save' => 'Opslaan',
|
||||
'language_exists' => 'Het kenmerk :attribute bestaat al.',
|
||||
'uh_oh' => 'Er klopt iets niet helemaal',
|
||||
'group_single' => 'Groep / Enkelvoudig',
|
||||
'group' => 'Groep',
|
||||
'single' => 'Enkelvoudig',
|
||||
'value' => 'Waarde',
|
||||
'namespace' => 'Namespace',
|
||||
'add_translation' => 'Voeg een vertaling toe',
|
||||
'translation_added' => 'Nieuwe vertaling succesvol toegevoegd 🙌',
|
||||
'namespace_label' => 'Namespace (optioneel)',
|
||||
'group_label' => 'Groep (optioneel)',
|
||||
'key_label' => 'Sleutel',
|
||||
'value_label' => 'Waarde',
|
||||
'namespace_placeholder' => 'bijv. Mijn_pakket',
|
||||
'group_placeholder' => 'bijv. bevestiging',
|
||||
'key_placeholder' => 'bijv. ongeldige sleutel',
|
||||
'value_placeholder' => 'bijv. Sleutels mogen geen spaties bevatten',
|
||||
'advanced_options' => 'Schakel geavanceerde opties in',
|
||||
];
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="search">
|
||||
<input type="text" class="search-input" placeholder="{{ __('translation::translation.search') }}" name="{{ $name }}" value="{{ $value }}">
|
||||
</div>
|
||||
@@ -0,0 +1,18 @@
|
||||
<div class="select-group">
|
||||
|
||||
<select name="{{ $name }}" @if(isset($submit) && $submit) v-on:change="submit" @endif>
|
||||
@if(isset($optional) && $optional)<option value> ----- </option>@endif
|
||||
@foreach($items as $key => $value)
|
||||
@if(is_numeric($key))
|
||||
<option value="{{ $value }}" @if(isset($selected) && $selected === $value) selected="selected" @endif>{{ $value }}</option>
|
||||
@else
|
||||
<option value="{{ $key }}" @if(isset($selected) && $selected === $key) selected="selected" @endif>{{ $value }}</option>
|
||||
@endif
|
||||
@endforeach
|
||||
</select>
|
||||
|
||||
<div class="caret">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"/></svg>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,18 @@
|
||||
<div class="input-group">
|
||||
<label for="{{ $field }}">
|
||||
{{ $label }}
|
||||
</label>
|
||||
<input
|
||||
class="@if($errors->has($field)) error @endif"
|
||||
name="{{ $field }}"
|
||||
id="{{ $field }}"
|
||||
type="text"
|
||||
placeholder="{{ isset($placeholder) ? $placeholder : '' }}"
|
||||
value="{{ old($field) }}"
|
||||
{{ isset($required) ? 'required' : '' }}>
|
||||
@if($errors->has($field))
|
||||
@foreach($errors->get($field) as $error)
|
||||
<p class="error-text">{!! $error !!}</p>
|
||||
@endforeach
|
||||
@endif
|
||||
</div>
|
||||
@@ -0,0 +1 @@
|
||||
<svg class="{{ isset($class) ? $class : 'fill-current w-4 h-4 mr-2' }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm2-2.25a8 8 0 0 0 4-2.46V9a2 2 0 0 1-2-2V3.07a7.95 7.95 0 0 0-3-1V3a2 2 0 0 1-2 2v1a2 2 0 0 1-2 2v2h3a2 2 0 0 1 2 2v5.75zm-4 0V15a2 2 0 0 1-2-2v-1h-.5A1.5 1.5 0 0 1 4 10.5V8H2.25A8.01 8.01 0 0 0 8 17.75z"/></svg>
|
||||
|
After Width: | Height: | Size: 389 B |
@@ -0,0 +1 @@
|
||||
<svg class="{{ isset($class) ? $class : 'fill-current w-4 h-4 mr-2' }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M7.41 9l2.24 2.24-.83 2L6 10.4l-3.3 3.3-1.4-1.42L4.58 9l-.88-.88c-.53-.53-1-1.3-1.3-2.12h2.2c.15.28.33.53.51.7l.89.9.88-.88C7.48 6.1 8 4.84 8 4H0V2h5V0h2v2h5v2h-2c0 1.37-.74 3.15-1.7 4.12L7.4 9zm3.84 8L10 20H8l5-12h2l5 12h-2l-1.25-3h-5.5zm.83-2h3.84L14 10.4 12.08 15z"/></svg>
|
||||
|
After Width: | Height: | Size: 412 B |
@@ -0,0 +1,41 @@
|
||||
@extends('translation::layout')
|
||||
|
||||
@section('body')
|
||||
|
||||
<div class="panel w-1/2">
|
||||
|
||||
<div class="panel-header">
|
||||
|
||||
{{ __('translation::translation.add_language') }}
|
||||
|
||||
</div>
|
||||
|
||||
<form action="{{ route('languages.store') }}" method="POST">
|
||||
|
||||
<fieldset>
|
||||
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}">
|
||||
|
||||
<div class="panel-body p-4">
|
||||
|
||||
@include('translation::forms.text', ['field' => 'name', 'label' => __('translation::translation.language_name'), ])
|
||||
|
||||
@include('translation::forms.text', ['field' => 'locale', 'label' => __('translation::translation.locale'), ])
|
||||
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
|
||||
<div class="panel-footer flex flex-row-reverse">
|
||||
|
||||
<button class="button button-blue">
|
||||
{{ __('translation::translation.save') }}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
@@ -0,0 +1,56 @@
|
||||
@extends('translation::layout')
|
||||
|
||||
@section('body')
|
||||
|
||||
@if(count($languages))
|
||||
|
||||
<div class="panel w-1/2">
|
||||
|
||||
<div class="panel-header">
|
||||
|
||||
{{ __('translation::translation.languages') }}
|
||||
|
||||
<div class="flex flex-grow justify-end items-center">
|
||||
|
||||
<a href="{{ route('languages.create') }}" class="button">
|
||||
{{ __('translation::translation.add') }}
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
|
||||
<table>
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ __('translation::translation.language_name') }}</th>
|
||||
<th>{{ __('translation::translation.locale') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach($languages as $language => $name)
|
||||
<tr>
|
||||
<td>
|
||||
{{ $name }}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ route('languages.translations.index', $language) }}">
|
||||
{{ $language }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@endif
|
||||
|
||||
@endsection
|
||||
@@ -0,0 +1,56 @@
|
||||
@extends('translation::layout')
|
||||
|
||||
@section('body')
|
||||
|
||||
<div class="panel w-1/2">
|
||||
|
||||
<div class="panel-header">
|
||||
|
||||
{{ __('translation::translation.add_translation') }}
|
||||
|
||||
</div>
|
||||
|
||||
<form action="{{ route('languages.translations.store', $language) }}" method="POST">
|
||||
|
||||
<fieldset>
|
||||
|
||||
<input type="hidden" name="_token" value="{{ csrf_token() }}">
|
||||
|
||||
<div class="panel-body p-4">
|
||||
|
||||
@include('translation::forms.text', ['field' => 'group', 'label' => __('translation::translation.group_label'), 'placeholder' => __('translation::translation.group_placeholder')])
|
||||
|
||||
@include('translation::forms.text', ['field' => 'key', 'label' => __('translation::translation.key_label'), 'placeholder' => __('translation::translation.key_placeholder')])
|
||||
|
||||
@include('translation::forms.text', ['field' => 'value', 'label' => __('translation::translation.value_label'), 'placeholder' => __('translation::translation.value_placeholder')])
|
||||
|
||||
<div class="input-group">
|
||||
|
||||
<button v-on:click="toggleAdvancedOptions" class="text-blue">{{ __('translation::translation.advanced_options') }}</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div v-show="showAdvancedOptions">
|
||||
|
||||
@include('translation::forms.text', ['field' => 'namespace', 'label' => __('translation::translation.namespace_label'), 'placeholder' => __('translation::translation.namespace_placeholder')])
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
|
||||
<div class="panel-footer flex flex-row-reverse">
|
||||
|
||||
<button class="button button-blue">
|
||||
{{ __('translation::translation.save') }}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
@@ -0,0 +1,89 @@
|
||||
@extends('translation::layout')
|
||||
|
||||
@section('body')
|
||||
|
||||
<form action="{{ route('languages.translations.index', ['language' => $language]) }}" method="get">
|
||||
|
||||
<div class="panel">
|
||||
|
||||
<div class="panel-header">
|
||||
|
||||
{{ __('translation::translation.translations') }}
|
||||
|
||||
<div class="flex flex-grow justify-end items-center">
|
||||
|
||||
@include('translation::forms.search', ['name' => 'filter', 'value' => Request::get('filter')])
|
||||
|
||||
@include('translation::forms.select', ['name' => 'language', 'items' => $languages, 'submit' => true, 'selected' => $language])
|
||||
|
||||
<div class="sm:hidden lg:flex items-center">
|
||||
|
||||
@include('translation::forms.select', ['name' => 'group', 'items' => $groups, 'submit' => true, 'selected' => Request::get('group'), 'optional' => true])
|
||||
|
||||
<a href="{{ route('languages.translations.create', $language) }}" class="button">
|
||||
{{ __('translation::translation.add') }}
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
|
||||
@if(count($translations))
|
||||
|
||||
<table>
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-1/5 uppercase font-thin">{{ __('translation::translation.group_single') }}</th>
|
||||
<th class="w-1/5 uppercase font-thin">{{ __('translation::translation.key') }}</th>
|
||||
<th class="uppercase font-thin">{{ config('app.locale') }}</th>
|
||||
<th class="uppercase font-thin">{{ $language }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach($translations as $type => $items)
|
||||
|
||||
@foreach($items as $group => $translations)
|
||||
|
||||
@foreach($translations as $key => $value)
|
||||
|
||||
@if(!is_array($value[config('app.locale')]))
|
||||
<tr>
|
||||
<td>{{ $group }}</td>
|
||||
<td>{{ $key }}</td>
|
||||
<td>{{ $value[config('app.locale')] }}</td>
|
||||
<td>
|
||||
<translation-input
|
||||
initial-translation="{{ $value[$language] }}"
|
||||
language="{{ $language }}"
|
||||
group="{{ $group }}"
|
||||
translation-key="{{ $key }}"
|
||||
route="{{ config('translation.ui_url') }}">
|
||||
</translation-input>
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
|
||||
@endforeach
|
||||
|
||||
@endforeach
|
||||
|
||||
@endforeach
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
@endsection
|
||||
24
support/laravel-translation/resources/views/layout.blade.php
Normal file
24
support/laravel-translation/resources/views/layout.blade.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>{{ config('app.name') }}</title>
|
||||
<link rel="stylesheet" href="{{ asset('/vendor/translation/css/main.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
|
||||
@include('translation::nav')
|
||||
@include('translation::notifications')
|
||||
|
||||
@yield('body')
|
||||
|
||||
</div>
|
||||
|
||||
<script src="{{ asset('/vendor/translation/js/app.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
20
support/laravel-translation/resources/views/nav.blade.php
Normal file
20
support/laravel-translation/resources/views/nav.blade.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<nav class="header">
|
||||
|
||||
<h1 class="text-lg px-6">{{ config('app.name') }}</h1>
|
||||
|
||||
<ul class="flex-grow justify-end pr-2">
|
||||
<li>
|
||||
<a href="{{ route('languages.index') }}" class="{{ set_active('') }}{{ set_active('/create') }}">
|
||||
@include('translation::icons.globe')
|
||||
{{ __('translation::translation.languages') }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('languages.translations.index', config('app.locale')) }}" class="{{ set_active('*/translations') }}">
|
||||
@include('translation::icons.translate')
|
||||
{{ __('translation::translation.translations') }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
@@ -0,0 +1,15 @@
|
||||
@if(Session::has('success'))
|
||||
<div class="bg-green-lightest text-green-darker p-6 shadow-md" role="alert">
|
||||
<div class="flex justify-center">
|
||||
<p>{{ Session::get('success') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(Session::has('error'))
|
||||
<div class="bg-red-lightest text-red-darker p-6 shadow-md" role="alert">
|
||||
<div class="flex justify-center">
|
||||
<p>{!! Session::get('error') !!}</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
24
support/laravel-translation/routes/web.php
Normal file
24
support/laravel-translation/routes/web.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
Route::group(config('translation.route_group_config') + ['namespace' => 'JoeDixon\\Translation\\Http\\Controllers'], function ($router) {
|
||||
$router->get(config('translation.ui_url'), 'LanguageController@index')
|
||||
->name('languages.index');
|
||||
|
||||
$router->get(config('translation.ui_url').'/create', 'LanguageController@create')
|
||||
->name('languages.create');
|
||||
|
||||
$router->post(config('translation.ui_url'), 'LanguageController@store')
|
||||
->name('languages.store');
|
||||
|
||||
$router->get(config('translation.ui_url').'/{language}/translations', 'LanguageTranslationController@index')
|
||||
->name('languages.translations.index');
|
||||
|
||||
$router->post(config('translation.ui_url').'/{language}', 'LanguageTranslationController@update')
|
||||
->name('languages.translations.update');
|
||||
|
||||
$router->get(config('translation.ui_url').'/{language}/translations/create', 'LanguageTranslationController@create')
|
||||
->name('languages.translations.create');
|
||||
|
||||
$router->post(config('translation.ui_url').'/{language}/translations', 'LanguageTranslationController@store')
|
||||
->name('languages.translations.store');
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace JoeDixon\Translation\Console\Commands;
|
||||
|
||||
class AddLanguageCommand extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'translation:add-language';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Add a new language to the application';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// ask the user for the language they wish to add
|
||||
$language = $this->ask(__('translation::translation.prompt_language'));
|
||||
$name = $this->ask(__('translation::translation.prompt_name'));
|
||||
|
||||
// attempt to add the key and fail gracefully if exception thrown
|
||||
try {
|
||||
$this->translation->addLanguage($language, $name);
|
||||
$this->info(__('translation::translation.language_added'));
|
||||
} catch (\Exception $e) {
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace JoeDixon\Translation\Console\Commands;
|
||||
|
||||
class AddTranslationKeyCommand extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'translation:add-translation-key';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Add a new language key for the application';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$language = $this->ask(__('translation::translation.prompt_language_for_key'));
|
||||
|
||||
// we know this should be single or group so we can use the `anticipate`
|
||||
// method to give our users a helping hand
|
||||
$type = $this->anticipate(__('translation::translation.prompt_type'), ['single', 'group']);
|
||||
|
||||
// if the group type is selected, prompt for the group key
|
||||
if ($type === 'group') {
|
||||
$file = $this->ask(__('translation::translation.prompt_group'));
|
||||
}
|
||||
$key = $this->ask(__('translation::translation.prompt_key'));
|
||||
$value = $this->ask(__('translation::translation.prompt_value'));
|
||||
|
||||
// attempt to add the key for single or group and fail gracefully if
|
||||
// exception is thrown
|
||||
if ($type === 'single') {
|
||||
try {
|
||||
$this->translation->addSingleTranslation($language, 'single', $key, $value);
|
||||
|
||||
return $this->info(__('translation::translation.language_key_added'));
|
||||
} catch (\Exception $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
} elseif ($type === 'group') {
|
||||
try {
|
||||
$file = str_replace('.php', '', $file);
|
||||
$this->translation->addGroupTranslation($language, $file, $key, $value);
|
||||
|
||||
return $this->info(__('translation::translation.language_key_added'));
|
||||
} catch (\Exception $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
} else {
|
||||
return $this->error(__('translation::translation.type_error'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace JoeDixon\Translation\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use JoeDixon\Translation\Drivers\Translation;
|
||||
|
||||
class BaseCommand extends Command
|
||||
{
|
||||
protected $translation;
|
||||
|
||||
public function __construct(Translation $translation)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->translation = $translation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace JoeDixon\Translation\Console\Commands;
|
||||
|
||||
class ListLanguagesCommand extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'translation:list-languages';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'List all of the available languages in the application';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$headers = [__('translation::translation.language_name'), __('translation::translation.language')];
|
||||
$languages = $this->translation->allLanguages()->toArray();
|
||||
$mappedLanguages = [];
|
||||
|
||||
foreach ($languages as $language => $name) {
|
||||
$mappedLanguages[] = [$name, $language];
|
||||
}
|
||||
|
||||
// return a table of results
|
||||
$this->table($headers, $mappedLanguages);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace JoeDixon\Translation\Console\Commands;
|
||||
|
||||
class ListMissingTranslationKeys extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'translation:list-missing-translation-keys';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'List all of the translation keys in the app which don\'t have a corresponding translation';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$missingTranslations = [];
|
||||
$rows = [];
|
||||
|
||||
foreach ($this->translation->allLanguages() as $language => $name) {
|
||||
$missingTranslations[$language] = $this->translation->findMissingTranslations($language);
|
||||
}
|
||||
|
||||
// check whether or not there are any missing translations
|
||||
$empty = true;
|
||||
foreach ($missingTranslations as $language => $values) {
|
||||
if (! empty($values)) {
|
||||
$empty = false;
|
||||
}
|
||||
}
|
||||
|
||||
// if no missing translations, inform the user and move on with your day
|
||||
if ($empty) {
|
||||
return $this->info(__('translation::translation.no_missing_keys'));
|
||||
}
|
||||
|
||||
// set some headers for the table of results
|
||||
$headers = [__('translation::translation.language'), __('translation::translation.type'), __('translation::translation.group'), __('translation::translation.key')];
|
||||
|
||||
// iterate over each of the missing languages
|
||||
foreach ($missingTranslations as $language => $types) {
|
||||
// iterate over each of the file types (json or array)
|
||||
foreach ($types as $type => $keys) {
|
||||
// iterate over each of the keys
|
||||
foreach ($keys as $key => $value) {
|
||||
// populate the array with the relevant data to fill the table
|
||||
foreach ($value as $k => $v) {
|
||||
$rows[] = [$language, $type, $key, $k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// render the table of results
|
||||
$this->table($headers, $rows);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace JoeDixon\Translation\Console\Commands;
|
||||
|
||||
class SynchroniseMissingTranslationKeys extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'translation:sync-missing-translation-keys {language?}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Add all of the missing translation keys for all languages or a single language';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$language = $this->argument('language') ?: false;
|
||||
|
||||
try {
|
||||
// if we have a language, pass it in, if not the method will
|
||||
// automagically sync all languages
|
||||
$this->translation->saveMissingTranslations($language);
|
||||
|
||||
return $this->info(__('translation::translation.keys_synced'));
|
||||
} catch (\Exception $e) {
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
namespace JoeDixon\Translation\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use JoeDixon\Translation\Drivers\Database;
|
||||
use JoeDixon\Translation\Drivers\File;
|
||||
use JoeDixon\Translation\Drivers\Translation;
|
||||
use JoeDixon\Translation\Scanner;
|
||||
|
||||
class SynchroniseTranslationsCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'translation:sync-translations {from?} {to?} {language?}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Synchronise translations between drivers';
|
||||
|
||||
/**
|
||||
* File scanner.
|
||||
*
|
||||
* @var Scanner
|
||||
*/
|
||||
private $scanner;
|
||||
|
||||
/**
|
||||
* Translation.
|
||||
*
|
||||
* @var Translation
|
||||
*/
|
||||
private $translation;
|
||||
|
||||
/**
|
||||
* From driver.
|
||||
*/
|
||||
private $fromDriver;
|
||||
|
||||
/**
|
||||
* To driver.
|
||||
*/
|
||||
private $toDriver;
|
||||
|
||||
/**
|
||||
* Translation drivers.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $drivers = ['file', 'database'];
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Scanner $scanner, Translation $translation)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->scanner = $scanner;
|
||||
$this->translation = $translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$languages = array_keys($this->translation->allLanguages()->toArray());
|
||||
|
||||
// If a valid from driver has been specified as an argument.
|
||||
if ($this->argument('from') && in_array($this->argument('from'), $this->drivers)) {
|
||||
$this->fromDriver = $this->argument('from');
|
||||
}
|
||||
|
||||
// When the from driver will be entered manually or if the argument is invalid.
|
||||
else {
|
||||
$this->fromDriver = $this->anticipate('Which driver would you like to take translations from?', $this->drivers);
|
||||
|
||||
if (! in_array($this->fromDriver, $this->drivers)) {
|
||||
return $this->error('Invalid driver');
|
||||
}
|
||||
}
|
||||
|
||||
// Create the driver.
|
||||
$this->fromDriver = $this->createDriver($this->fromDriver);
|
||||
|
||||
// When the to driver has been specified.
|
||||
if ($this->argument('to') && in_array($this->argument('to'), $this->drivers)) {
|
||||
$this->toDriver = $this->argument('to');
|
||||
}
|
||||
|
||||
// When the to driver will be entered manually.
|
||||
else {
|
||||
$this->toDriver = $this->anticipate('Which driver would you like to add the translations to?', $this->drivers);
|
||||
|
||||
if (! in_array($this->toDriver, $this->drivers)) {
|
||||
return $this->error('Invalid driver');
|
||||
}
|
||||
}
|
||||
|
||||
// Create the driver.
|
||||
$this->toDriver = $this->createDriver($this->toDriver);
|
||||
|
||||
// If the language argument is set.
|
||||
if ($this->argument('language')) {
|
||||
|
||||
// If all languages should be synced.
|
||||
if ($this->argument('language') == 'all') {
|
||||
$language = false;
|
||||
}
|
||||
// When a specific language is set and is valid.
|
||||
elseif (in_array($this->argument('language'), $languages)) {
|
||||
$language = $this->argument('language');
|
||||
} else {
|
||||
return $this->error('Invalid language');
|
||||
}
|
||||
} // When the language will be entered manually or if the argument is invalid.
|
||||
else {
|
||||
$language = $this->anticipate('Which language? (leave blank for all)', $languages);
|
||||
|
||||
if ($language && ! in_array($language, $languages)) {
|
||||
return $this->error('Invalid language');
|
||||
}
|
||||
}
|
||||
|
||||
$this->line('Syncing translations');
|
||||
|
||||
// If a specific language is set.
|
||||
if ($language) {
|
||||
$this->mergeTranslations($this->toDriver, $language, $this->fromDriver->allTranslationsFor($language));
|
||||
} // Else process all languages.
|
||||
else {
|
||||
$translations = $this->mergeLanguages($this->toDriver, $this->fromDriver->allTranslations());
|
||||
}
|
||||
|
||||
$this->info('Translations have been synced');
|
||||
}
|
||||
|
||||
private function createDriver($driver)
|
||||
{
|
||||
if ($driver === 'file') {
|
||||
return new File(new Filesystem, app('path.lang'), config('app.locale'), $this->scanner);
|
||||
}
|
||||
|
||||
return new Database(config('app.locale'), $this->scanner);
|
||||
}
|
||||
|
||||
private function mergeLanguages($driver, $languages)
|
||||
{
|
||||
foreach ($languages as $language => $translations) {
|
||||
$this->mergeTranslations($driver, $language, $translations);
|
||||
}
|
||||
}
|
||||
|
||||
private function mergeTranslations($driver, $language, $translations)
|
||||
{
|
||||
$this->mergeGroupTranslations($driver, $language, $translations['group']);
|
||||
$this->mergeSingleTranslations($driver, $language, $translations['single']);
|
||||
}
|
||||
|
||||
private function mergeGroupTranslations($driver, $language, $groups)
|
||||
{
|
||||
foreach ($groups as $group => $translations) {
|
||||
foreach ($translations as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
continue;
|
||||
}
|
||||
$driver->addGroupTranslation($language, $group, $key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function mergeSingleTranslations($driver, $language, $vendors)
|
||||
{
|
||||
foreach ($vendors as $vendor => $translations) {
|
||||
foreach ($translations as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
continue;
|
||||
}
|
||||
$driver->addSingleTranslation($language, $vendor, $key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
74
support/laravel-translation/src/ContractDatabaseLoader.php
Normal file
74
support/laravel-translation/src/ContractDatabaseLoader.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace JoeDixon\Translation;
|
||||
|
||||
use Illuminate\Contracts\Translation\Loader;
|
||||
use JoeDixon\Translation\Drivers\Translation;
|
||||
|
||||
class ContractDatabaseLoader implements Loader
|
||||
{
|
||||
private $translation;
|
||||
|
||||
public function __construct(Translation $translation)
|
||||
{
|
||||
$this->translation = $translation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the messages for the given locale.
|
||||
*
|
||||
* @param string $locale
|
||||
* @param string $group
|
||||
* @param string $namespace
|
||||
* @return array
|
||||
*/
|
||||
public function load($locale, $group, $namespace = null)
|
||||
{
|
||||
if ($group == '*' && $namespace == '*') {
|
||||
return $this->translation->getSingleTranslationsFor($locale)->get('single', collect())->toArray();
|
||||
}
|
||||
|
||||
if (is_null($namespace) || $namespace == '*') {
|
||||
return $this->translation->getGroupTranslationsFor($locale)->filter(function ($value, $key) use ($group) {
|
||||
return $key === $group;
|
||||
})->first();
|
||||
}
|
||||
|
||||
return $this->translation->getGroupTranslationsFor($locale)->filter(function ($value, $key) use ($group, $namespace) {
|
||||
return $key === "{$namespace}::{$group}";
|
||||
})->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new namespace to the loader.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param string $hint
|
||||
* @return void
|
||||
*/
|
||||
public function addNamespace($namespace, $hint)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new JSON path to the loader.
|
||||
*
|
||||
* @param string $path
|
||||
* @return void
|
||||
*/
|
||||
public function addJsonPath($path)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of all the registered namespaces.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function namespaces()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
279
support/laravel-translation/src/Drivers/Database.php
Normal file
279
support/laravel-translation/src/Drivers/Database.php
Normal file
@@ -0,0 +1,279 @@
|
||||
<?php
|
||||
|
||||
namespace JoeDixon\Translation\Drivers;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use JoeDixon\Translation\Exceptions\LanguageExistsException;
|
||||
use JoeDixon\Translation\Language;
|
||||
use JoeDixon\Translation\Translation as TranslationModel;
|
||||
use Throwable;
|
||||
|
||||
class Database extends Translation implements DriverInterface
|
||||
{
|
||||
protected $sourceLanguage;
|
||||
|
||||
protected $scanner;
|
||||
|
||||
protected array $groupTranslationCache = [];
|
||||
|
||||
protected array $languageCache = [];
|
||||
|
||||
public function __construct($sourceLanguage, $scanner)
|
||||
{
|
||||
$this->sourceLanguage = $sourceLanguage;
|
||||
$this->scanner = $scanner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all languages from the application.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function allLanguages()
|
||||
{
|
||||
return Language::all()->mapWithKeys(function ($language) {
|
||||
return [$language->language => $language->name ?: $language->language];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all group translations from the application.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function allGroup($language)
|
||||
{
|
||||
$groups = TranslationModel::getGroupsForLanguage($language);
|
||||
|
||||
return $groups->map(function ($translation) {
|
||||
return $translation->group;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the translations from the application.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function allTranslations()
|
||||
{
|
||||
return $this->allLanguages()->mapWithKeys(function ($name, $language) {
|
||||
return [$language => $this->allTranslationsFor($language)];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all translations for a particular language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Collection
|
||||
*/
|
||||
public function allTranslationsFor($language)
|
||||
{
|
||||
return Collection::make([
|
||||
'group' => $this->getGroupTranslationsFor($language),
|
||||
'single' => $this->getSingleTranslationsFor($language),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new language to the application.
|
||||
*
|
||||
* @param string $language
|
||||
* @return void
|
||||
*/
|
||||
public function addLanguage($language, $name = null)
|
||||
{
|
||||
if ($this->languageExists($language)) {
|
||||
throw new LanguageExistsException(__('translation::errors.language_exists', ['language' => $language]));
|
||||
}
|
||||
|
||||
Language::create([
|
||||
'language' => $language,
|
||||
'name' => $name,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new group type translation.
|
||||
*
|
||||
* @param string $language
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function addGroupTranslation($language, $group, $key, $value = '')
|
||||
{
|
||||
if (! $this->languageExists($language)) {
|
||||
$this->addLanguage($language);
|
||||
}
|
||||
|
||||
Language::where('language', $language)
|
||||
->first()
|
||||
->translations()
|
||||
->updateOrCreate([
|
||||
'group' => $group,
|
||||
'key' => $key,
|
||||
], [
|
||||
'group' => $group,
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new single type translation.
|
||||
*
|
||||
* @param string $language
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function addSingleTranslation($language, $vendor, $key, $value = '')
|
||||
{
|
||||
if (! $this->languageExists($language)) {
|
||||
$this->addLanguage($language);
|
||||
}
|
||||
|
||||
Language::where('language', $language)
|
||||
->first()
|
||||
->translations()
|
||||
->updateOrCreate([
|
||||
'group' => $vendor,
|
||||
'key' => $key,
|
||||
], [
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the single translations for a given language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Collection
|
||||
*/
|
||||
public function getSingleTranslationsFor($language)
|
||||
{
|
||||
$translations = $this->getLanguage($language)
|
||||
->translations()
|
||||
->where('group', 'like', '%single')
|
||||
->orWhereNull('group')
|
||||
->get()
|
||||
->groupBy('group');
|
||||
|
||||
// if there is no group, this is a legacy translation so we need to
|
||||
// update to 'single'. We do this here so it only happens once.
|
||||
if ($this->hasLegacyGroups($translations->keys())) {
|
||||
TranslationModel::whereNull('group')->update(['group' => 'single']);
|
||||
// if any legacy groups exist, rerun the method so we get the
|
||||
// updated keys.
|
||||
return $this->getSingleTranslationsFor($language);
|
||||
}
|
||||
|
||||
return $translations->map(function ($translations, $group) {
|
||||
return $translations->mapWithKeys(function ($translation) {
|
||||
return [$translation->key => $translation->value];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the group translations for a given language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Collection
|
||||
*/
|
||||
public function getGroupTranslationsFor($language)
|
||||
{
|
||||
if (isset($this->groupTranslationCache[$language])) {
|
||||
return $this->groupTranslationCache[$language];
|
||||
}
|
||||
|
||||
$languageModel = $this->getLanguage($language);
|
||||
|
||||
if (is_null($languageModel)) {
|
||||
return collect();
|
||||
}
|
||||
|
||||
$translations = $languageModel
|
||||
->translations()
|
||||
->whereNotNull('group')
|
||||
->where('group', 'not like', '%single')
|
||||
->get()
|
||||
->groupBy('group');
|
||||
|
||||
$result = $translations->map(function ($translations) {
|
||||
return $translations->mapWithKeys(function ($translation) {
|
||||
return [$translation->key => $translation->value];
|
||||
});
|
||||
});
|
||||
|
||||
$this->groupTranslationCache[$language] = $result;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not a language exists.
|
||||
*
|
||||
* @param string $language
|
||||
* @return bool
|
||||
*/
|
||||
public function languageExists($language)
|
||||
{
|
||||
return $this->getLanguage($language) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of group names for a given language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Collection
|
||||
*/
|
||||
public function getGroupsFor($language)
|
||||
{
|
||||
return $this->allGroup($language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a language from the database.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Language
|
||||
*/
|
||||
private function getLanguage($language)
|
||||
{
|
||||
if (isset($this->languageCache[$language])) {
|
||||
return $this->languageCache[$language];
|
||||
}
|
||||
|
||||
// Some constallation of composer packages can lead to our code being executed
|
||||
// as a dependency of running migrations. That's why we need to be able to
|
||||
// handle the case where the database is empty / our tables don't exist:
|
||||
try {
|
||||
$result = Language::where('language', $language)->first();
|
||||
} catch (Throwable) {
|
||||
$result = null;
|
||||
}
|
||||
|
||||
$this->languageCache[$language] = $result;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a set of single translations contains any legacy groups.
|
||||
* Previously, this was handled by setting the group value to NULL, now
|
||||
* we use 'single' to cater for vendor JSON language files.
|
||||
*
|
||||
* @param Collection $groups
|
||||
* @return bool
|
||||
*/
|
||||
private function hasLegacyGroups($groups)
|
||||
{
|
||||
return $groups->filter(function ($key) {
|
||||
return $key === '';
|
||||
})->count() > 0;
|
||||
}
|
||||
}
|
||||
128
support/laravel-translation/src/Drivers/DriverInterface.php
Normal file
128
support/laravel-translation/src/Drivers/DriverInterface.php
Normal file
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace JoeDixon\Translation\Drivers;
|
||||
|
||||
interface DriverInterface
|
||||
{
|
||||
/**
|
||||
* Get all languages from the application.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function allLanguages();
|
||||
|
||||
/**
|
||||
* Get all group translations from the application.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function allGroup($language);
|
||||
|
||||
/**
|
||||
* Get all the translations from the application.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function allTranslations();
|
||||
|
||||
/**
|
||||
* Get all translations for a particular language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Collection
|
||||
*/
|
||||
public function allTranslationsFor($language);
|
||||
|
||||
/**
|
||||
* Add a new language to the application.
|
||||
*
|
||||
* @param string $language
|
||||
* @return void
|
||||
*/
|
||||
public function addLanguage($language, $name = null);
|
||||
|
||||
/**
|
||||
* Add a new group type translation.
|
||||
*
|
||||
* @param string $language
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function addGroupTranslation($language, $group, $key, $value = '');
|
||||
|
||||
/**
|
||||
* Add a new single type translation.
|
||||
*
|
||||
* @param string $language
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function addSingleTranslation($language, $vendor, $key, $value = '');
|
||||
|
||||
/**
|
||||
* Get all of the single translations for a given language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Collection
|
||||
*/
|
||||
public function getSingleTranslationsFor($language);
|
||||
|
||||
/**
|
||||
* Get all of the group translations for a given language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Collection
|
||||
*/
|
||||
public function getGroupTranslationsFor($language);
|
||||
|
||||
/**
|
||||
* Determine whether or not a language exists.
|
||||
*
|
||||
* @param string $language
|
||||
* @return bool
|
||||
*/
|
||||
public function languageExists($language);
|
||||
|
||||
/**
|
||||
* Find all of the translations in the app without translation for a given language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return array
|
||||
*/
|
||||
public function findMissingTranslations($language);
|
||||
|
||||
/**
|
||||
* Save all of the translations in the app without translation for a given language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return void
|
||||
*/
|
||||
public function saveMissingTranslations($language = false);
|
||||
|
||||
/**
|
||||
* Get a collection of group names for a given language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Collection
|
||||
*/
|
||||
public function getGroupsFor($language);
|
||||
|
||||
/**
|
||||
* Get all translations for a given language merged with the source language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Collection
|
||||
*/
|
||||
public function getSourceLanguageTranslationsWith($language);
|
||||
|
||||
/**
|
||||
* Filter all keys and translations for a given language and string.
|
||||
*
|
||||
* @param string $language
|
||||
* @param string $filter
|
||||
* @return Collection
|
||||
*/
|
||||
public function filterTranslationsFor($language, $filter);
|
||||
}
|
||||
369
support/laravel-translation/src/Drivers/File.php
Normal file
369
support/laravel-translation/src/Drivers/File.php
Normal file
@@ -0,0 +1,369 @@
|
||||
<?php
|
||||
|
||||
namespace JoeDixon\Translation\Drivers;
|
||||
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use JoeDixon\Translation\Exceptions\LanguageExistsException;
|
||||
|
||||
class File extends Translation implements DriverInterface
|
||||
{
|
||||
private $disk;
|
||||
|
||||
private $languageFilesPath;
|
||||
|
||||
protected $sourceLanguage;
|
||||
|
||||
protected $scanner;
|
||||
|
||||
public function __construct(Filesystem $disk, $languageFilesPath, $sourceLanguage, $scanner)
|
||||
{
|
||||
$this->disk = $disk;
|
||||
$this->languageFilesPath = $languageFilesPath;
|
||||
$this->sourceLanguage = $sourceLanguage;
|
||||
$this->scanner = $scanner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all languages from the application.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function allLanguages()
|
||||
{
|
||||
// As per the docs, there should be a subdirectory within the
|
||||
// languages path so we can return these directory names as a collection
|
||||
$directories = Collection::make($this->disk->directories($this->languageFilesPath));
|
||||
|
||||
return $directories->mapWithKeys(function ($directory) {
|
||||
$language = basename($directory);
|
||||
|
||||
return [$language => $language];
|
||||
})->filter(function ($language) {
|
||||
// at the moemnt, we're not supporting vendor specific translations
|
||||
return $language != 'vendor';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all group translations from the application.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function allGroup($language)
|
||||
{
|
||||
$groupPath = "{$this->languageFilesPath}".DIRECTORY_SEPARATOR."{$language}";
|
||||
|
||||
if (! $this->disk->exists($groupPath)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$groups = Collection::make($this->disk->allFiles($groupPath));
|
||||
|
||||
return $groups->map(function ($group) {
|
||||
return $group->getBasename('.php');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the translations from the application.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function allTranslations()
|
||||
{
|
||||
return $this->allLanguages()->mapWithKeys(function ($language) {
|
||||
return [$language => $this->allTranslationsFor($language)];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all translations for a particular language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Collection
|
||||
*/
|
||||
public function allTranslationsFor($language)
|
||||
{
|
||||
return Collection::make([
|
||||
'group' => $this->getGroupTranslationsFor($language),
|
||||
'single' => $this->getSingleTranslationsFor($language),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new language to the application.
|
||||
*
|
||||
* @param string $language
|
||||
* @return void
|
||||
*/
|
||||
public function addLanguage($language, $name = null)
|
||||
{
|
||||
if ($this->languageExists($language)) {
|
||||
throw new LanguageExistsException(__('translation::errors.language_exists', ['language' => $language]));
|
||||
}
|
||||
|
||||
$this->disk->makeDirectory("{$this->languageFilesPath}".DIRECTORY_SEPARATOR."$language");
|
||||
if (! $this->disk->exists("{$this->languageFilesPath}".DIRECTORY_SEPARATOR."{$language}.json")) {
|
||||
$this->saveSingleTranslations($language, collect(['single' => collect()]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new group type translation.
|
||||
*
|
||||
* @param string $language
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function addGroupTranslation($language, $group, $key, $value = '')
|
||||
{
|
||||
if (! $this->languageExists($language)) {
|
||||
$this->addLanguage($language);
|
||||
}
|
||||
|
||||
$translations = $this->getGroupTranslationsFor($language);
|
||||
|
||||
// does the group exist? If not, create it.
|
||||
if (! $translations->keys()->contains($group)) {
|
||||
$translations->put($group, collect());
|
||||
}
|
||||
|
||||
$values = $translations->get($group);
|
||||
$values[$key] = $value;
|
||||
$translations->put($group, collect($values));
|
||||
|
||||
$this->saveGroupTranslations($language, $group, $translations->get($group));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new single type translation.
|
||||
*
|
||||
* @param string $language
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function addSingleTranslation($language, $vendor, $key, $value = '')
|
||||
{
|
||||
if (! $this->languageExists($language)) {
|
||||
$this->addLanguage($language);
|
||||
}
|
||||
|
||||
$translations = $this->getSingleTranslationsFor($language);
|
||||
$translations->get($vendor) ?: $translations->put($vendor, collect());
|
||||
$translations->get($vendor)->put($key, $value);
|
||||
|
||||
$this->saveSingleTranslations($language, $translations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the single translations for a given language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Collection
|
||||
*/
|
||||
public function getSingleTranslationsFor($language)
|
||||
{
|
||||
$files = new Collection($this->disk->allFiles($this->languageFilesPath));
|
||||
|
||||
return $files->filter(function ($file) use ($language) {
|
||||
return strpos($file, "{$language}.json");
|
||||
})->flatMap(function ($file) {
|
||||
if (strpos($file->getPathname(), 'vendor')) {
|
||||
$vendor = Str::before(Str::after($file->getPathname(), 'vendor'.DIRECTORY_SEPARATOR), DIRECTORY_SEPARATOR);
|
||||
|
||||
return ["{$vendor}::single" => new Collection(json_decode($this->disk->get($file), true))];
|
||||
}
|
||||
|
||||
return ['single' => new Collection(json_decode($this->disk->get($file), true))];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the group translations for a given language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Collection
|
||||
*/
|
||||
public function getGroupTranslationsFor($language)
|
||||
{
|
||||
return $this->getGroupFilesFor($language)->mapWithKeys(function ($group) {
|
||||
// here we check if the path contains 'vendor' as these will be the
|
||||
// files which need namespacing
|
||||
if (Str::contains($group->getPathname(), 'vendor')) {
|
||||
$vendor = Str::before(Str::after($group->getPathname(), 'vendor'.DIRECTORY_SEPARATOR), DIRECTORY_SEPARATOR);
|
||||
|
||||
return ["{$vendor}::{$group->getBasename('.php')}" => new Collection(Arr::dot($this->disk->getRequire($group->getPathname())))];
|
||||
}
|
||||
|
||||
return [$group->getBasename('.php') => new Collection(Arr::dot($this->disk->getRequire($group->getPathname())))];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the translations for a given file.
|
||||
*
|
||||
* @param string $language
|
||||
* @param string $file
|
||||
* @return array
|
||||
*/
|
||||
public function getTranslationsForFile($language, $file)
|
||||
{
|
||||
$file = Str::finish($file, '.php');
|
||||
$filePath = "{$this->languageFilesPath}".DIRECTORY_SEPARATOR."{$language}".DIRECTORY_SEPARATOR."{$file}";
|
||||
$translations = [];
|
||||
|
||||
if ($this->disk->exists($filePath)) {
|
||||
$translations = Arr::dot($this->disk->getRequire($filePath));
|
||||
}
|
||||
|
||||
return $translations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not a language exists.
|
||||
*
|
||||
* @param string $language
|
||||
* @return bool
|
||||
*/
|
||||
public function languageExists($language)
|
||||
{
|
||||
return $this->allLanguages()->contains($language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new group of translations.
|
||||
*
|
||||
* @param string $language
|
||||
* @param string $group
|
||||
* @return void
|
||||
*/
|
||||
public function addGroup($language, $group)
|
||||
{
|
||||
$this->saveGroupTranslations($language, $group, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save group type language translations.
|
||||
*
|
||||
* @param string $language
|
||||
* @param string $group
|
||||
* @param array $translations
|
||||
* @return void
|
||||
*/
|
||||
public function saveGroupTranslations($language, $group, $translations)
|
||||
{
|
||||
// here we check if it's a namespaced translation which need saving to a
|
||||
// different path
|
||||
$translations = $translations instanceof Collection ? $translations->toArray() : $translations;
|
||||
ksort($translations);
|
||||
$translations = array_undot($translations);
|
||||
if (Str::contains($group, '::')) {
|
||||
return $this->saveNamespacedGroupTranslations($language, $group, $translations);
|
||||
}
|
||||
$this->disk->put("{$this->languageFilesPath}".DIRECTORY_SEPARATOR."{$language}".DIRECTORY_SEPARATOR."{$group}.php", "<?php\n\nreturn ".var_export($translations, true).';'.\PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save namespaced group type language translations.
|
||||
*
|
||||
* @param string $language
|
||||
* @param string $group
|
||||
* @param array $translations
|
||||
* @return void
|
||||
*/
|
||||
private function saveNamespacedGroupTranslations($language, $group, $translations)
|
||||
{
|
||||
[$namespace, $group] = explode('::', $group);
|
||||
$directory = "{$this->languageFilesPath}".DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR."{$namespace}".DIRECTORY_SEPARATOR."{$language}";
|
||||
|
||||
if (! $this->disk->exists($directory)) {
|
||||
$this->disk->makeDirectory($directory, 0755, true);
|
||||
}
|
||||
|
||||
$this->disk->put("$directory".DIRECTORY_SEPARATOR."{$group}.php", "<?php\n\nreturn ".var_export($translations, true).';'.\PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save single type language translations.
|
||||
*
|
||||
* @param string $language
|
||||
* @param array $translations
|
||||
* @return void
|
||||
*/
|
||||
private function saveSingleTranslations($language, $translations)
|
||||
{
|
||||
foreach ($translations as $group => $translation) {
|
||||
$vendor = Str::before($group, '::single');
|
||||
$languageFilePath = $vendor !== 'single' ? 'vendor'.DIRECTORY_SEPARATOR."{$vendor}".DIRECTORY_SEPARATOR."{$language}.json" : "{$language}.json";
|
||||
$this->disk->put(
|
||||
"{$this->languageFilesPath}".DIRECTORY_SEPARATOR."{$languageFilePath}",
|
||||
json_encode((object) $translations->get($group), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the group files for a given language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Collection
|
||||
*/
|
||||
public function getGroupFilesFor($language)
|
||||
{
|
||||
$groups = new Collection($this->disk->allFiles("{$this->languageFilesPath}".DIRECTORY_SEPARATOR."{$language}"));
|
||||
// namespaced files reside in the vendor directory so we'll grab these
|
||||
// the `getVendorGroupFileFor` method
|
||||
$groups = $groups->merge($this->getVendorGroupFilesFor($language));
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection of group names for a given language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Collection
|
||||
*/
|
||||
public function getGroupsFor($language)
|
||||
{
|
||||
return $this->getGroupFilesFor($language)->map(function ($file) {
|
||||
if (Str::contains($file->getPathname(), 'vendor')) {
|
||||
$vendor = Str::before(Str::after($file->getPathname(), 'vendor'.DIRECTORY_SEPARATOR), DIRECTORY_SEPARATOR);
|
||||
|
||||
return "{$vendor}::{$file->getBasename('.php')}";
|
||||
}
|
||||
|
||||
return $file->getBasename('.php');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the vendor group files for a given language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Collection
|
||||
*/
|
||||
public function getVendorGroupFilesFor($language)
|
||||
{
|
||||
if (! $this->disk->exists("{$this->languageFilesPath}".DIRECTORY_SEPARATOR.'vendor')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$vendorGroups = [];
|
||||
foreach ($this->disk->directories("{$this->languageFilesPath}".DIRECTORY_SEPARATOR.'vendor') as $vendor) {
|
||||
$vendor = Arr::last(explode(DIRECTORY_SEPARATOR, $vendor));
|
||||
if (! $this->disk->exists("{$this->languageFilesPath}".DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR."{$vendor}".DIRECTORY_SEPARATOR."{$language}")) {
|
||||
array_push($vendorGroups, []);
|
||||
} else {
|
||||
array_push($vendorGroups, $this->disk->allFiles("{$this->languageFilesPath}".DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR."{$vendor}".DIRECTORY_SEPARATOR."{$language}"));
|
||||
}
|
||||
}
|
||||
|
||||
return new Collection(Arr::flatten($vendorGroups));
|
||||
}
|
||||
}
|
||||
120
support/laravel-translation/src/Drivers/Translation.php
Normal file
120
support/laravel-translation/src/Drivers/Translation.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace JoeDixon\Translation\Drivers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Str;
|
||||
use JoeDixon\Translation\Events\TranslationAdded;
|
||||
|
||||
abstract class Translation
|
||||
{
|
||||
/**
|
||||
* Find all of the translations in the app without translation for a given language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return array
|
||||
*/
|
||||
public function findMissingTranslations($language)
|
||||
{
|
||||
return array_diff_assoc_recursive(
|
||||
$this->scanner->findTranslations(),
|
||||
$this->allTranslationsFor($language)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save all of the translations in the app without translation for a given language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return void
|
||||
*/
|
||||
public function saveMissingTranslations($language = false)
|
||||
{
|
||||
$languages = $language ? [$language => $language] : $this->allLanguages();
|
||||
|
||||
foreach ($languages as $language => $name) {
|
||||
$missingTranslations = $this->findMissingTranslations($language);
|
||||
|
||||
foreach ($missingTranslations as $type => $groups) {
|
||||
foreach ($groups as $group => $translations) {
|
||||
foreach ($translations as $key => $value) {
|
||||
if (Str::contains($group, 'single')) {
|
||||
$this->addSingleTranslation($language, $group, $key);
|
||||
} else {
|
||||
$this->addGroupTranslation($language, $group, $key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all translations for a given language merged with the source language.
|
||||
*
|
||||
* @param string $language
|
||||
* @return Collection
|
||||
*/
|
||||
public function getSourceLanguageTranslationsWith($language)
|
||||
{
|
||||
$sourceTranslations = $this->allTranslationsFor($this->sourceLanguage);
|
||||
$languageTranslations = $this->allTranslationsFor($language);
|
||||
|
||||
return $sourceTranslations->map(function ($groups, $type) use ($language, $languageTranslations) {
|
||||
return $groups->map(function ($translations, $group) use ($type, $language, $languageTranslations) {
|
||||
$translations = $translations->toArray();
|
||||
array_walk($translations, function (&$value, $key) use ($type, $group, $language, $languageTranslations) {
|
||||
$value = [
|
||||
$this->sourceLanguage => $value,
|
||||
$language => $languageTranslations->get($type, collect())->get($group, collect())->get($key),
|
||||
];
|
||||
});
|
||||
|
||||
return $translations;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter all keys and translations for a given language and string.
|
||||
*
|
||||
* @param string $language
|
||||
* @param string $filter
|
||||
* @return Collection
|
||||
*/
|
||||
public function filterTranslationsFor($language, $filter)
|
||||
{
|
||||
$allTranslations = $this->getSourceLanguageTranslationsWith(($language));
|
||||
if (! $filter) {
|
||||
return $allTranslations;
|
||||
}
|
||||
|
||||
return $allTranslations->map(function ($groups, $type) use ($language, $filter) {
|
||||
return $groups->map(function ($keys, $group) use ($language, $filter) {
|
||||
return collect($keys)->filter(function ($translations, $key) use ($group, $language, $filter) {
|
||||
return strs_contain([$group, $key, $translations[$language], $translations[$this->sourceLanguage]], $filter);
|
||||
});
|
||||
})->filter(function ($keys) {
|
||||
return $keys->isNotEmpty();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public function add(Request $request, $language, $isGroupTranslation)
|
||||
{
|
||||
$namespace = $request->has('namespace') && $request->get('namespace') ? "{$request->get('namespace')}::" : '';
|
||||
$group = $namespace.$request->get('group');
|
||||
$key = $request->get('key');
|
||||
$value = $request->get('value') ?: '';
|
||||
|
||||
if ($isGroupTranslation) {
|
||||
$this->addGroupTranslation($language, $group, $key, $value);
|
||||
} else {
|
||||
$this->addSingleTranslation($language, 'single', $key, $value);
|
||||
}
|
||||
|
||||
Event::dispatch(new TranslationAdded($language, $group ?: 'single', $key, $value));
|
||||
}
|
||||
}
|
||||
28
support/laravel-translation/src/Events/TranslationAdded.php
Normal file
28
support/laravel-translation/src/Events/TranslationAdded.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace JoeDixon\Translation\Events;
|
||||
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
|
||||
class TranslationAdded
|
||||
{
|
||||
use Dispatchable;
|
||||
|
||||
public $key;
|
||||
public $group;
|
||||
public $value;
|
||||
public $language;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(string $language, string $group, string $key, string $value)
|
||||
{
|
||||
$this->language = $language;
|
||||
$this->group = $group;
|
||||
$this->key = $key;
|
||||
$this->value = $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace JoeDixon\Translation\Exceptions;
|
||||
|
||||
class LanguageExistsException extends \Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace JoeDixon\Translation\Exceptions;
|
||||
|
||||
class LanguageKeyExistsException extends \Exception
|
||||
{
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user