adding a bunch of wip: i18n stuff
This commit is contained in:
83
src/app/Console/Commands/TranslationCheckerCommand.php
Normal file
83
src/app/Console/Commands/TranslationCheckerCommand.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class TranslationCheckerCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'i18n:check-json {--l|locale= : The source of truth locale}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Uses one JSON file as the source of truth to check other lang files against.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$masterLocale = 'en';
|
||||
if (!empty($this->option('locale'))) {
|
||||
$masterLocale = trim($this->option('locale'));
|
||||
}
|
||||
|
||||
$langDir = base_path('lang');
|
||||
$langFiles = scandir($langDir);
|
||||
$bigCount = count($langFiles);
|
||||
for ($i = 0; $i < $bigCount; $i++) {
|
||||
if (! preg_match('/.*\.json$/', $langFiles[$i]) || "{$masterLocale}.json" === $langFiles[$i]) {
|
||||
unset($langFiles[$i]);
|
||||
}
|
||||
}
|
||||
|
||||
$langFilesCount = count($langFiles);
|
||||
$this->info("Checking {$langFilesCount} file(s) against {$masterLocale}...");
|
||||
|
||||
$masterLocaleTranslations = translations(base_path("lang/{$masterLocale}.json"));
|
||||
|
||||
foreach ($langFiles as $localeFile) {
|
||||
$otherLocaleTranslations = translations(base_path("lang/{$localeFile}"));
|
||||
$counts = 0;
|
||||
$mergedTranslations = $this->recursiveMergeArray($masterLocaleTranslations, $otherLocaleTranslations, $counts);
|
||||
file_put_contents(base_path("lang/{$localeFile}"), json_encode($mergedTranslations, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT));
|
||||
$this->info("{$localeFile} had {$counts} missing translations.");
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
public function recursiveMergeArray(array $firstArray, array $secondArray, int &$counts)
|
||||
{
|
||||
$mergedArray = $secondArray;
|
||||
foreach ($firstArray as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
if (!array_key_exists($key, $secondArray)) {
|
||||
$mergedArray[$key] = $value;
|
||||
} else {
|
||||
$subDiff = $this->recursiveMergeArray($value, $secondArray[$key], $counts);
|
||||
if (!empty($subDiff)) {
|
||||
$mergedArray[$key] = $subDiff;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!array_key_exists($key, $secondArray)) {
|
||||
$counts++;
|
||||
$mergedArray[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $mergedArray;
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Models\Language;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Middleware;
|
||||
|
||||
@ -41,19 +42,56 @@ class HandleInertiaRequests extends Middleware
|
||||
*/
|
||||
public function share(Request $request): array
|
||||
{
|
||||
$localeFields = ['locale', 'iso_code', 'name', 'localized_name'];
|
||||
$currentLocale = $request->session()->get('locale', null);
|
||||
if (is_null($currentLocale)) {
|
||||
$currentLocale = Language::where(['locale' => 'en', 'iso_code' => 'en_US'])->get($localeFields)[0]->toArray();
|
||||
$request->session()->put('locale', [
|
||||
'locale' => $currentLocale['locale'],
|
||||
'iso_code' => $currentLocale['iso_code'],
|
||||
'name' => $currentLocale['name'],
|
||||
'localized_name' => $currentLocale['localized_name'],
|
||||
]);
|
||||
}
|
||||
$localeFilePath = base_path("lang/{$currentLocale['locale']}.json");
|
||||
|
||||
$notifications = [];
|
||||
$notificationsCount = count($notifications);
|
||||
$unreadNotifications = false;
|
||||
if (! is_null($request->user())) {
|
||||
$notifications = $request->user()->notifications;
|
||||
$notificationsCount = count($notifications);
|
||||
for ($i = 0; $i < $notificationsCount; $i++) {
|
||||
$newData = $notifications[$i]->data;
|
||||
$createdAt = carbon($notifications[$i]->created_at);
|
||||
$dateFormat = 'F j';
|
||||
if (!$createdAt->is(gmdate('Y'))) {
|
||||
$dateFormat = 'F j, Y';
|
||||
}
|
||||
$newData['created_at_date'] = $createdAt->copy()->format($dateFormat);
|
||||
$newData['created_at_time'] = $createdAt->copy()->format('H:i');
|
||||
$notifications[$i]->data = $newData;
|
||||
}
|
||||
if (count($request->user()->unreadNotifications) > 0) {
|
||||
$unreadNotifications = true;
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge(parent::share($request), [
|
||||
'appName' => config('app.name'),
|
||||
$additionalData = [
|
||||
'appName' => config('app.name'),
|
||||
|
||||
'availableLocales' => Language::get($localeFields),
|
||||
'currentLocale' => $currentLocale,
|
||||
'language' => translations($localeFilePath),
|
||||
|
||||
'notifications' => $notifications,
|
||||
'unreadNotifications' => $unreadNotifications,
|
||||
]);
|
||||
];
|
||||
|
||||
if ($request->user()->email === env('ADMIN_EMAIL')) {
|
||||
$additionalData['is_admin_user'] = true;
|
||||
}
|
||||
|
||||
return array_merge(parent::share($request), $additionalData);
|
||||
}
|
||||
}
|
||||
|
29
src/app/Http/Middleware/SetLocale.php
Normal file
29
src/app/Http/Middleware/SetLocale.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class SetLocale
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @package App\Http\Middleware\SetLocale
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
||||
*
|
||||
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (session()->has('locale')) {
|
||||
app()->setLocale(session('locale')['locale']);
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
@ -37,6 +37,7 @@ class User extends Authenticatable
|
||||
'name',
|
||||
'surname',
|
||||
'timezone_name',
|
||||
'language_id',
|
||||
'current_team_id',
|
||||
'profile_photo_path',
|
||||
'email',
|
||||
@ -183,4 +184,17 @@ class User extends Authenticatable
|
||||
{
|
||||
return $this->morphOne(Address::class, 'addressable');
|
||||
}
|
||||
|
||||
/**
|
||||
* Language relationship.
|
||||
*
|
||||
* @package App\Models\User
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function language(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Language::class);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Language;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
@ -31,6 +32,7 @@ class UserFactory extends Factory
|
||||
'email' => $this->faker->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'timezone_name' => $this->faker->timezone(),
|
||||
'timezone_name' => Language::all()->random()->id,
|
||||
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
||||
'remember_token' => Str::random(10),
|
||||
];
|
||||
|
@ -16,49 +16,18 @@ class LanguageSeeder extends Seeder
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$languages = [
|
||||
[
|
||||
'iso_code' => 'en_US',
|
||||
'locale' => 'en',
|
||||
'title' => 'English',
|
||||
'title_localized' => 'English',
|
||||
],
|
||||
[
|
||||
'iso_code' => 'de_DE',
|
||||
'locale' => 'de',
|
||||
'title' => 'German',
|
||||
'title_localized' => 'Deutsch',
|
||||
],
|
||||
[
|
||||
'iso_code' => 'fr_FR',
|
||||
'locale' => 'fr',
|
||||
'title' => 'French',
|
||||
'title_localized' => 'Français',
|
||||
],
|
||||
[
|
||||
'iso_code' => 'es_SP',
|
||||
'locale' => 'es',
|
||||
'title' => 'Spanish',
|
||||
'title_localized' => 'Español',
|
||||
],
|
||||
[
|
||||
'iso_code' => 'jp_JP',
|
||||
'locale' => 'jp',
|
||||
'title' => 'Japanese',
|
||||
'title_localized' => '日本',
|
||||
],
|
||||
[
|
||||
'iso_code' => 'zh_TW',
|
||||
'locale' => 'zh',
|
||||
'title' => 'Taiwanese',
|
||||
'title_localized' => '台湾',
|
||||
],
|
||||
$languagesData = [
|
||||
['locale' => 'en', 'iso_code' => 'en_US', 'name' => 'English', 'localized_name' => 'English (US)'],
|
||||
['locale' => 'de', 'iso_code' => 'de_DE', 'name' => 'German', 'localized_name' => 'Deutsch'],
|
||||
['locale' => 'fr', 'iso_code' => 'fr_FR', 'name' => 'French', 'localized_name' => 'Français'],
|
||||
['locale' => 'jp', 'iso_code' => 'jp_JP', 'name' => 'Japanese', 'localized_name' => '日本'],
|
||||
['locale' => 'mx', 'iso_code' => 'es_MX', 'name' => 'Spanish', 'localized_name' => 'Español'],
|
||||
];
|
||||
|
||||
$startTime = Carbon::now();
|
||||
$offset = 0;
|
||||
|
||||
foreach ($languages as $language) {
|
||||
foreach ($languagesData as $language) {
|
||||
$datetime = $startTime->copy()->addMinute($offset)->toDateTimeString();
|
||||
$offset++;
|
||||
$language['created_at'] = $datetime;
|
||||
|
@ -1,3 +1,88 @@
|
||||
{
|
||||
"key": "translation that has :attribute in the string."
|
||||
"titles": {
|
||||
"welcome": "Welcome",
|
||||
"dashboard": "Dashboard",
|
||||
"secure_area": "Secure Area",
|
||||
"forgot_password": "Forgot Password",
|
||||
"my_profile": "My Profile",
|
||||
"edit_profile": "Edit Profile",
|
||||
"log_in": "Log In To Your Account",
|
||||
"register": "Register A New Account",
|
||||
"reset_password": "Reset Password",
|
||||
"two_factor_confirmation": "Two-factor Confirmation",
|
||||
"verify_email": "Email Verification"
|
||||
},
|
||||
"labels": {
|
||||
"type": "Type",
|
||||
"none": "None",
|
||||
"title": "Title",
|
||||
"name": "Name",
|
||||
"created_at": "Created At",
|
||||
"updated_at": "Updated At",
|
||||
"actions": "Actions",
|
||||
"email": "Email",
|
||||
"timezone_name": "Timezone",
|
||||
"language": "Language",
|
||||
"password": "Password",
|
||||
"confirm_password": "Confirm Password",
|
||||
"current_password": "Current Password",
|
||||
"remember_me": "Remember me",
|
||||
"first_name": "First Name",
|
||||
"last_name": "Surname",
|
||||
"forgot_password": "Forgot your password?",
|
||||
"already_registered": "Already registered?",
|
||||
"code": "Code",
|
||||
"recovery_code": "Recovery Code",
|
||||
"use_recovery_code": "Use a recovery code",
|
||||
"use_authentication_code": "Use an authentication code",
|
||||
"email_verification": "Email Verification",
|
||||
"user": "User",
|
||||
"optional": "optional"
|
||||
},
|
||||
"actions": {
|
||||
"save": "Save",
|
||||
"update": "Update",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"confirm": "Confirm",
|
||||
"log_in": "Log in",
|
||||
"log_out": "Log Out",
|
||||
"register": "Register",
|
||||
"edit_profile": "Edit Profile",
|
||||
"confirm_password": "Confirm Password",
|
||||
"reset_password": "Reset Password",
|
||||
"email_password_reset_link": "Email Password Reset Link",
|
||||
"resend_verification_email": "Resend Verification Email"
|
||||
},
|
||||
"nav": {
|
||||
"manage_team": "Manage Team",
|
||||
"team_settings": "Team Settings",
|
||||
"create_new_team": "Create New Team",
|
||||
"switch_teams": "Switch Teams",
|
||||
"manage_account": "Manage Account",
|
||||
"profile": "My Profile",
|
||||
"admin_settings": "Admin Settings",
|
||||
"api_tokens": "API Tokens"
|
||||
},
|
||||
"pagination": {
|
||||
"show_from_to_count": "Showing :from to :to of :count results"
|
||||
},
|
||||
"notifications": {
|
||||
"saved": "Saved.",
|
||||
"verified": "Verified!",
|
||||
"unverified": "Unverified.",
|
||||
"new_email_verification_sent": "A new verification link has been sent to the email address you provided in your profile settings."
|
||||
},
|
||||
"pages": {
|
||||
"profile": {
|
||||
"titles": {
|
||||
"update_password": "Update Your Password"
|
||||
},
|
||||
"text_blocks": {
|
||||
"unverified_message": "Please verify your account.",
|
||||
"verified_message": "Thanks for being amazing.",
|
||||
"secure_password": "Ensure your account is using a long, random password to stay secure."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,24 @@
|
||||
{
|
||||
"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"
|
||||
"dev": "vite",
|
||||
"build": "vite build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"laravel-mix": "^6.0.49",
|
||||
"@vitejs/plugin-vue": "^3.0.0",
|
||||
"laravel-vite-plugin": "^0.6.0",
|
||||
"postcss": "^8.4.14",
|
||||
"postcss-import": "^14.1.0",
|
||||
"vue-loader": "^17.0.0"
|
||||
"vite": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@inertiajs/inertia": "^0.11.0",
|
||||
"@inertiajs/inertia-vue3": "^0.6.0",
|
||||
"@inertiajs/progress": "^0.2.7",
|
||||
"@tailwindcss/aspect-ratio": "^0.4.0",
|
||||
"@tailwindcss/forms": "^0.5.2",
|
||||
"@tailwindcss/line-clamp": "^0.4.2",
|
||||
"@tailwindcss/typography": "^0.5.2",
|
||||
"@vueuse/core": "^8.7.5",
|
||||
"apexcharts": "^3.35.5",
|
||||
"axios": "^0.27.2",
|
||||
"daisyui": "^2.29.0",
|
||||
"dayjs": "^1.11.3",
|
||||
|
@ -5,6 +5,7 @@ import { createInertiaApp } from '@inertiajs/inertia-vue3';
|
||||
import { InertiaProgress } from '@inertiajs/progress';
|
||||
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
|
||||
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';
|
||||
import translationHelper from './base.js';
|
||||
import Notifications from 'notiwind';
|
||||
import AppLayout from './Layouts/AppLayout.vue';
|
||||
import AuthLayout from './Layouts/AuthLayout.vue';
|
||||
@ -43,6 +44,7 @@ createInertiaApp({
|
||||
.use(plugin)
|
||||
.use(Notifications)
|
||||
.use(ZiggyVue, Ziggy)
|
||||
.mixin(translationHelper)
|
||||
.mixin({ methods: { route } })
|
||||
.mount(el);
|
||||
},
|
||||
|
30
src/resources/js/base.js
Normal file
30
src/resources/js/base.js
Normal file
@ -0,0 +1,30 @@
|
||||
export default {
|
||||
methods: {
|
||||
__(key, replace = {}) {
|
||||
let translation = ''
|
||||
|
||||
// check for dot notation
|
||||
if (key.includes('.')) {
|
||||
translation = this.$page.props.language
|
||||
key.split('.').forEach(subKey => {
|
||||
translation = translation[subKey]
|
||||
}, key)
|
||||
} else {
|
||||
// check if it exists on the translated strings we got
|
||||
if (this.$page.props.language.hasOwnProperty(key)) {
|
||||
translation = this.$page.props.language[key]
|
||||
} else {
|
||||
// otherwise just take it raw
|
||||
translation = key
|
||||
}
|
||||
}
|
||||
|
||||
// used to fill in variables for translation string
|
||||
Object.keys(replace).forEach(key => {
|
||||
translation = translation.replace(':' + key, replace[key])
|
||||
});
|
||||
|
||||
return translation;
|
||||
},
|
||||
},
|
||||
};
|
Reference in New Issue
Block a user