huge Laravel 10 upgrade

This commit is contained in:
HolgerHatGarKeineNode
2023-02-19 20:13:20 +01:00
parent 5c74f77beb
commit 12847f95f6
440 changed files with 46336 additions and 682 deletions

View 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;
}
}

View 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);
}

View 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));
}
}

View 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));
}
}