perf: 🌐 Migliorata gestione traduzioni e localizzazione

This commit is contained in:
Maicol Battistini 2023-05-05 15:02:04 +02:00
parent 9895ada867
commit a281356b08
No known key found for this signature in database
13 changed files with 38 additions and 289 deletions

View File

@ -13,8 +13,6 @@ use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\File;
use Nette\Utils\Json;
use Nette\Utils\JsonException;
use ReflectionClass;
class Controller extends BaseController
@ -28,8 +26,9 @@ class Controller extends BaseController
$locale = $request->input('locale');
$languages = self::getLanguages();
if ($languages->contains($locale)) {
session()->put('locale', $locale);
session()->save();
$request->session()->put('locale', $locale);
$request->session()->save();
$request->user()?->settings()->set('locale', $locale);
app()->setLocale($locale);
return response()->json(['locale' => app()->getLocale()]);
@ -44,20 +43,10 @@ class Controller extends BaseController
public static function getLanguages(): Collection
{
return collect(File::glob(lang_path('*.json')))
->merge(File::directories(lang_path()))
->map(static fn (string $file) => File::name($file));
}
/**
* @return Collection<string>
*
* @throws JsonException
*/
public static function getTranslations(): Collection
{
return self::getLanguages()
->mapWithKeys(fn (string $locale) => [$locale => Json::decode(File::get(lang_path("$locale.json")))]);
}
/**
* @return Collection<array{
* name: string,

View File

@ -9,7 +9,12 @@ class LocaleMiddleware
{
public function handle(Request $request, Closure $next): mixed
{
app()->setLocale(session()->get('locale', app()->getLocale()));
app()->setLocale($request
->user()
?->settings()
->get('locale', app()->getLocale())
?? session('locale', app()->getLocale())
);
return $next($request);
}

View File

@ -1,32 +0,0 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Root Namespace
|--------------------------------------------------------------------------
|
| The root JSON:API namespace, within your application's namespace.
| This is used when generating any class that does not sit *within*
| a server's namespace. For example, new servers and filters.
|
| By default this is set to `JsonApi` which means the root namespace
| will be `\App\JsonApi`, if your application's namespace is `App`.
*/
'namespace' => 'JsonApi',
/*
|--------------------------------------------------------------------------
| Servers
|--------------------------------------------------------------------------
|
| A list of the JSON:API compliant APIs in your application, referred to
| as "servers". They must be listed below, with the array key being the
| unique name for each server, and the value being the fully-qualified
| class name of the server class.
*/
'servers' => [
'v1' => \App\JsonApi\V1\Server::class,
],
];

View File

@ -1,45 +0,0 @@
<?php
return [
// Directories to search in.
'directories' => [
'app',
'resources/ts',
'resources/views',
],
// File Patterns to search for.
'patterns' => [
'*.php',
'*.ts',
'*.tsx',
],
// Indicates whether new lines are allowed in translations.
'allow-newlines' => true,
// Translation function names.
// If your function name contains $ escape it using \$ .
'functions' => [
'__',
'_s',
'_v',
'@lang',
],
// Indicates whether you need to sort the translations alphabetically
// by original strings (keys).
// It helps navigate a translation file and detect possible duplicates.
'sort-keys' => true,
// Indicates whether keys from the persistent-strings file should be also added
// to translation files automatically on export if they don't yet exist there.
'add-persistent-strings-to-translations' => false,
// Indicates whether it's necessary to exclude Laravel translation keys
// from the resulting language file if they have corresponding translations
// in the given language.
// This option allows correctly combine two translation approaches:
// Laravel translation keys (PHP) and translatable strings (JSON).
'exclude-translation-keys' => true,
];

View File

@ -6,13 +6,15 @@ use Translator\Framework\LaravelConfigLoader;
use Translator\Infra\LaravelJsonTranslationRepository;
return [
'languages' => ['pt-br', 'es'],
'languages' => ['en'],
'directories' => [
app_path(),
resource_path('ts'),
resource_path('views'),
],
'output' => resource_path('lang'),
'extensions' => ['php'],
'output' => lang_path(),
'extensions' => ['php', 'ts', 'tsx'],
'functions' => ['__', '_s', '_v', '@lang'],
'container' => [
'config_loader' => LaravelConfigLoader::class,
'translation_repository' => LaravelJsonTranslationRepository::class,

View File

@ -1,112 +0,0 @@
{
"(and :count more error)": "(e :count altro errore)",
"(and :count more errors)": "(e :count altri errori)",
"* Campi obbligatori": "* Campi obbligatori",
"Accedi": "Accedi",
"Account creato con successo. Puoi ora accedere.": "Account creato con successo. Puoi ora accedere.",
"Aggiornamento del frontend disponibile!": "Aggiornamento del frontend disponibile!",
"Aggiungi": "Aggiungi",
"Aggiungi nuovo record": "Aggiungi nuovo record",
"All rights reserved.": "Tutti i diritti riservati",
"Annulla": "Annulla",
"assistenza ufficiale": "assistenza ufficiale",
"Bandiera della lingua :language": "Bandiera della lingua :language",
"Benvenuto in :name!": "Benvenuto in :name!",
"Cambia periodo": "Cambia periodo",
"Campi non validi. Controlla i dati inseriti": "Campi non validi. Controlla i dati inseriti",
"Conferma": "Conferma",
"Conferma password": "Conferma password",
"Connessione al database riuscita": "Connessione al database riuscita",
"Crea account": "Crea account",
"Creazione account amministratore": "Creazione account amministratore",
"Dashboard": "Dashboard",
"Database": "Database",
"Elimina": "Elimina",
"Email": "Email",
"Errore durante il salvataggio: :error": "Errore durante il salvataggio: :error",
"Errore durante l'eliminazione: :error": "Errore durante l'eliminazione: :error",
"Esci": "Esci",
"Esempio: :example": "Esempio: :example",
"Forbidden": "Vietato",
"Formato data corta": "Formato data corta",
"Formato data lunga": "Formato data lunga",
"Formato date": "Formato date",
"Formato orario": "Formato orario",
"forum": "forum",
"Go to page :page": "Vai alla pagina :page",
"Hello!": "Ciao!",
"Ho visionato e accetto la licenza": "Ho visionato e accetto la licenza",
"Host": "Host",
"I formati sono impostabili attraverso lo standard previsto da :link.": "I formati sono impostabili attraverso lo standard previsto da :link.",
"If you did not create an account, no further action is required.": "Se non hai creato un account, non è richiesta alcuna azione.",
"If you did not request a password reset, no further action is required.": "Se non hai richiesto un reset della password, non è richiesta alcuna azione.",
"If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "Se non riesci a cliccare sul pulsante \":actionText\", copia e incolla l'URL seguente\nnel tuo browser:",
"Il tuo profilo": "Il tuo profilo",
"Impossibile connettersi al database selezionato! Controllare il nome del database": "Impossibile connettersi al database selezionato! Controllare il nome del database",
"Impossibile scrivere il file di configurazione. :action": "Impossibile scrivere il file di configurazione. :action",
"Impostazioni salvate correttamente": "Impostazioni salvate correttamente",
"Inserisci le informazioni richieste per creare un nuovo account amministratore.": "Inserisci le informazioni richieste per creare un nuovo account amministratore.",
"L'utente del database non ha i seguenti permessi necessari: ": "L'utente del database non ha i seguenti permessi necessari: ",
"La password è stata inviata alla tua email": "La password è stata inviata alla tua email",
"Le credenziali non sono valide.": "Le credenziali non sono valide.",
"Le password non corrispondono": "Le password non corrispondono",
"Licenza": "Licenza",
"Lingua": "Lingua",
"Login": "Accedi",
"Logout": "Esci",
"No": "No",
"Nome database": "Nome database",
"Nome utente": "Nome utente",
"Nome utente/email": "Nome utente/email",
"Non sono presenti dati": "Non sono presenti dati",
"Not Found": "Non trovato",
"Notifiche": "Notifiche",
"of": "di",
"OpenSTAManager": "OpenSTAManager",
"OpenSTAManager è tutelato dalla licenza GPL 3.0, da accettare obbligatoriamente per poter utilizzare il gestionale.": "OpenSTAManager è tutelato dalla licenza GPL 3.0, da accettare obbligatoriamente per poter utilizzare il gestionale.",
"Page Expired": "Pagina scaduta",
"Pagination Navigation": "Navigazione della Paginazione",
"Password": "Password",
"Payment Required": "Pagamento richiesto",
"Please click the button below to verify your email address.": "Clicca sul pulsante qui sotto per verificare il tuo indirizzo email.",
"Record eliminato!": "Record eliminato!",
"Record salvato": "Record salvato",
"Regards": "Distinti saluti",
"Register": "Registrati",
"Reimposta password": "Reimposta password",
"Reset della password effettuato con successo. Puoi ora accedere.": "Reset della password effettuato con successo. Puoi ora accedere.",
"Reset Password": "Resetta la password",
"Reset Password Notification": "Notifica di reset della password",
"Resetta password": "Resetta password",
"results": "risultati",
"Ricarica": "Ricarica",
"Ricordami": "Ricordami",
"Salva e installa": "Salva e installa",
"Se necessiti supporto puoi contattarci tramite l':contactLink o tramite il nostro :forumLink.": "Se necessiti supporto puoi contattarci tramite l':contactLink o tramite il nostro :forumLink.",
"Sei sicuro di voler eliminare questo record?": "Sei sicuro di voler eliminare questo record?",
"Seleziona una voce dal menu a sinistra": "Seleziona una voce dal menu a sinistra",
"Server Error": "Errore server",
"Service Unavailable": "Servizio non disponibile",
"Showing": "Mostra",
"Si è verificato un errore durante la connessione al database: :error": "Si è verificato un errore durante la connessione al database: :error",
"Stampa": "Stampa",
"Sì": "Sì",
"Testa il database": "Testa il database",
"The :attribute must contain at least one letter.": ":Attribute deve contenere almeno una lettera.",
"The :attribute must contain at least one number.": ":Attribute deve contenere almeno un numero.",
"The :attribute must contain at least one symbol.": ":Attribute deve contenere almeno un carattere speciale.",
"The :attribute must contain at least one uppercase and one lowercase letter.": ":Attribute deve contenere almeno un carattere maiuscolo ed uno minuscolo.",
"The given :attribute has appeared in a data leak. Please choose a different :attribute.": ":Attribute sembra che faccia parte di un archivio con dati rubati. Per piacere, utilizza un valore differente.",
"The given data was invalid.": "I dati forniti non sono validi.",
"This password reset link will expire in :count minutes.": "Questo link di reset della password scadrà tra :count minuti.",
"to": "a",
"Toggle navigation": "Cambia navigazione",
"Too Many Requests": "Troppe richieste",
"Unauthorized": "Non autorizzato",
"Verify Email Address": "Verifica indirizzo email",
"Versione": "Versione",
"Versioni tradotte": "Versioni tradotte",
"Whoops!": "Ops!",
"You are receiving this email because we received a password reset request for your account.": "Hai ricevuto questa email perché abbiamo ricevuto una richiesta di reset della password per il tuo account.",
"È ora possibile lavorare offline!": "È ora possibile lavorare offline!"
}

View File

@ -63,7 +63,7 @@
"typed-scss-modules": "^7.1.0",
"typescript": "^5.0.4",
"vite": "^4.3.4",
"vite-plugin-laravel-translations": "^0.1.4",
"vite-plugin-laravel-translations": "github:maicol07/vite-plugin-laravel-translations#patch-1",
"vite-plugin-progress": "^0.0.7",
"vite-plugin-pwa": "^0.14.7"
},

View File

@ -10,21 +10,12 @@ dependencies:
'@maicol07/material-web-additions':
specifier: ^1.2.12
version: 1.2.12(@material/web@1.0.0-pre.7)
'@material/mwc-linear-progress':
specifier: ^0.27.0
version: 0.27.0
'@material/mwc-snackbar':
specifier: ^0.27.0
version: 0.27.0
'@material/mwc-top-app-bar':
specifier: ^0.27.0
version: 0.27.0
'@material/textfield':
specifier: ^14.0.0
version: 14.0.0
'@material/theme':
specifier: ^14.0.0
version: 14.0.0
'@material/web':
specifier: 1.0.0-pre.7
version: 1.0.0-pre.7
@ -148,8 +139,8 @@ devDependencies:
specifier: ^4.3.4
version: 4.3.4(@types/node@18.16.3)(sass@1.62.1)
vite-plugin-laravel-translations:
specifier: ^0.1.4
version: 0.1.4(vite@4.3.4)
specifier: github:maicol07/vite-plugin-laravel-translations#patch-1
version: github.com/maicol07/vite-plugin-laravel-translations/7f85c14e2f3d9f5d2aa860c720547dfb1a945e90(vite@4.3.4)
vite-plugin-progress:
specifier: ^0.0.7
version: 0.0.7(vite@4.3.4)
@ -1991,19 +1982,6 @@ packages:
tslib: 2.5.0
dev: false
/@material/linear-progress@14.0.0-canary.53b3cad2f.0:
resolution: { integrity: sha512-QqkDBcqX7TMt3zQn51LgS7K0y13rJ4ppMQL1f2uYBhDOov8nqndlaXw456KYE9RhU39JrLzVQlaAbU3eecVb/Q== }
dependencies:
'@material/animation': 14.0.0-canary.53b3cad2f.0
'@material/base': 14.0.0-canary.53b3cad2f.0
'@material/dom': 14.0.0-canary.53b3cad2f.0
'@material/feature-targeting': 14.0.0-canary.53b3cad2f.0
'@material/progress-indicator': 14.0.0-canary.53b3cad2f.0
'@material/rtl': 14.0.0-canary.53b3cad2f.0
'@material/theme': 14.0.0-canary.53b3cad2f.0
tslib: 2.5.0
dev: false
/@material/list@14.0.0:
resolution: { integrity: sha512-AFaBGV9vQyfnG8BT2R3UGVdF5w2SigQqBH+qbOSxQhk4BgVvhDfJUIKT415poLNMdnaDtcuYz+ZWvVNoRDaL7w== }
dependencies:
@ -2056,16 +2034,6 @@ packages:
tslib: 2.5.0
dev: false
/@material/mwc-linear-progress@0.27.0:
resolution: { integrity: sha512-kpWVLHPb7RzHU70Dq9bl1lBv05S49rQkyfFoL3PU5tEZHozT8IPQjdBLqAqd7H10cfLVjO7oIB+mVmvnFV+UsQ== }
dependencies:
'@material/linear-progress': 14.0.0-canary.53b3cad2f.0
'@material/mwc-base': 0.27.0
'@material/theme': 14.0.0-canary.53b3cad2f.0
lit: 2.7.4
tslib: 2.5.0
dev: false
/@material/mwc-snackbar@0.27.0:
resolution: { integrity: sha512-D9xhae4MiUwhHJxXaNzucmOutXVS/02CVnuCpzV2uDCLnY4oTaiZNDa+YBxheZnNKTTSm5yrshOnsRIQO6hgPw== }
dependencies:
@ -2102,12 +2070,6 @@ packages:
tslib: 2.5.0
dev: false
/@material/progress-indicator@14.0.0-canary.53b3cad2f.0:
resolution: { integrity: sha512-vW0oZK70QOpAarip95ueCQ/I3kBClcWjxsc0F0QjkqT76DOVXpjnZ4XoRRyq9eMpwLqlKLTecrsSNpmqwwF1Dg== }
dependencies:
tslib: 2.5.0
dev: false
/@material/ripple@14.0.0:
resolution: { integrity: sha512-9XoGBFd5JhFgELgW7pqtiLy+CnCIcV2s9cQ2BWbOQeA8faX9UZIDUx/g76nHLZ7UzKFtsULJxZTwORmsEt2zvw== }
dependencies:
@ -2206,26 +2168,6 @@ packages:
tslib: 2.5.0
dev: false
/@material/textfield@14.0.0:
resolution: { integrity: sha512-HGbtAlvlIB2vWBq85yw5wQeeP3Kndl6Z0TJzQ6piVtcfdl2mPyWhuuVHQRRAOis3rCIaAAaxCQYYTJh8wIi0XQ== }
dependencies:
'@material/animation': 14.0.0
'@material/base': 14.0.0
'@material/density': 14.0.0
'@material/dom': 14.0.0
'@material/feature-targeting': 14.0.0
'@material/floating-label': 14.0.0
'@material/line-ripple': 14.0.0
'@material/notched-outline': 14.0.0
'@material/ripple': 14.0.0
'@material/rtl': 14.0.0
'@material/shape': 14.0.0
'@material/theme': 14.0.0
'@material/tokens': 14.0.0
'@material/typography': 14.0.0
tslib: 2.5.0
dev: false
/@material/theme@14.0.0:
resolution: { integrity: sha512-6/SENWNIFuXzeHMPHrYwbsXKgkvCtWuzzQ3cUu4UEt3KcQ5YpViazIM6h8ByYKZP8A9d8QpkJ0WGX5btGDcVoA== }
dependencies:
@ -6425,17 +6367,6 @@ packages:
vite: 4.3.4(@types/node@18.16.3)(sass@1.62.1)
dev: true
/vite-plugin-laravel-translations@0.1.4(vite@4.3.4):
resolution: { integrity: sha512-3NT2ZbWZFppyRAKXvitkJriwxNsB8GA06U/jVJmUqtleNcuD24zJF6bqjMfGfnZN4s1zoAnR9P3OWrKIruRuHw== }
engines: { node: '>= 12.0.0', pnpm: '>= 7.0.0' }
peerDependencies:
vite: ^4.0.0
dependencies:
glob: 10.2.1
php-array-reader: 1.3.5
vite: 4.3.4(@types/node@18.16.3)(sass@1.62.1)
dev: true
/vite-plugin-progress@0.0.7(vite@4.3.4):
resolution: { integrity: sha512-zyvKdcc/X+6hnw3J1HVV1TKrlFKC4Rh8GnDnWG/2qhRXjqytTcM++xZ+SAPnoDsSyWl8O93ymK0wZRgHAoglEQ== }
engines: { node: '>=14', pnpm: '>=7.0.0' }
@ -6788,3 +6719,17 @@ packages:
- sugarss
- terser
dev: true
github.com/maicol07/vite-plugin-laravel-translations/7f85c14e2f3d9f5d2aa860c720547dfb1a945e90(vite@4.3.4):
resolution: { tarball: https://codeload.github.com/maicol07/vite-plugin-laravel-translations/tar.gz/7f85c14e2f3d9f5d2aa860c720547dfb1a945e90 }
id: github.com/maicol07/vite-plugin-laravel-translations/7f85c14e2f3d9f5d2aa860c720547dfb1a945e90
name: vite-plugin-laravel-translations
version: 0.1.4
engines: { node: '>= 12.0.0', pnpm: '>= 7.0.0' }
peerDependencies:
vite: ^4.0.0
dependencies:
glob: 10.2.1
php-array-reader: 1.3.5
vite: 4.3.4(@types/node@18.16.3)(sass@1.62.1)
dev: true

View File

@ -43,7 +43,7 @@ export default class WelcomeStep<A extends WelcomeStepAttributes = WelcomeStepAt
<div style={{textAlign: 'center'}}>
<h3>{__('Benvenuto!')}</h3>
<p>
{__('Puoi procedere alla configurazione tecnica del software attraverso i seguenti parametri, che potranno essere corretti secondo necessità tramite il file .env.')}
{__('Puoi procedere alla configurazione tecnica del software attraverso i seguenti parametri, che potranno essere corretti secondo necessità tramite il file .env.')}
<br/>
{_v('Se necessiti supporto puoi contattarci tramite l\':contactLink o tramite il nostro :forumLink.', {
contactLink: <a href="https://www.openstamanager.com/contattaci/?subject=Assistenza%20installazione%20OSM">{__('assistenza ufficiale')}</a>,
@ -96,7 +96,7 @@ export default class WelcomeStep<A extends WelcomeStepAttributes = WelcomeStepAt
const response = await Request.patch<{locale: string}>(route('app.language'), {locale});
app.locale = response.locale;
} catch (error: any) {
void showSnackbar(__('Si è verificato un errore durante il salvataggio della lingua: :error', {error: (error as RequestError).message}));
void showSnackbar(__('Si è verificato un errore durante il salvataggio della lingua: :error', {error: (error as RequestError<{message: string}>).response.message}));
}
m.redraw();
}

View File

@ -17,12 +17,13 @@ declare global {
let app: {
locale: string,
theme: 'high-contrast' | 'light', // TODO: Da implementare
translations: Record<string, Record<string, string>>,
user: OpenSTAManager.User | null,
VERSION: string,
REVISION: string,
};
const LARAVEL_TRANSLATIONS: Record<string, Record<string, string>>;
interface Window {
m: typeof Mithril;
tr: typeof translator;

View File

@ -31,7 +31,7 @@ export function tr<B extends boolean | undefined>(key: string, {
forceString
}: TranslationParameters<B>): Localized<B> {
let translation = key;
const translations = app.translations[app.locale];
const translations = LARAVEL_TRANSLATIONS[app.locale];
if (translations && translations[key]) {
translation = translations[key];

View File

@ -32,10 +32,6 @@
app = @js([
'locale' => app()->getLocale(),
'modules' => $modules,
'translations' => cache()->rememberForever(
'translations',
static fn () => \App\Http\Controllers\Controller::getTranslations()->toArray()
),
'user' => auth()->user(),
'VERSION' => trim(file_get_contents(base_path('VERSION'))),
'REVISION' => trim(file_get_contents(base_path('REVISION'))),

View File

@ -42,7 +42,7 @@ export default defineConfig(async () => {
refresh: true
}),
laravelTranslations({
namespace: 'osm',
namespace: false,
includeJson: true
}),
Inertia({