['\"])" . // Match " or ' and store in {quote} "(?P(?:\\\k{quote}|(?!\k{quote}).)*)" . // Match any string that can be {quote} escaped "\k{quote}" . // Match " or ' previously matched "[\),]"; // Close parentheses or new parameter // Find all PHP + Twig files in the app folder, except for storage $finder = new Finder(); $finder->in( $path )->exclude( 'storage' )->exclude( 'vendor' )->name( '*.php' )->name( '*.twig' )->name( '*.vue' )->files(); /** @var \Symfony\Component\Finder\SplFileInfo $file */ foreach ( $finder as $file ) { // Search the current file for the pattern if ( preg_match_all( "/$groupPattern/siU", $file->getContents(), $matches ) ) { // Get all matches foreach ( $matches[ 2 ] as $key ) { $groupKeys[] = $key; } } if ( preg_match_all( "/$stringPattern/siU", $file->getContents(), $matches ) ) { foreach ( $matches[ 'string' ] as $key ) { if ( preg_match( "/(^[a-zA-Z0-9_-]+([.][^\1)\ ]+)+$)/siU", $key, $groupMatches ) ) { // group{.group}.key format, already in $groupKeys but also matched here // do nothing, it has to be treated as a group continue; } //TODO: This can probably be done in the regex, but I couldn't do it. //skip keys which contain namespacing characters, unless they also contain a //space, which makes it JSON. if ( !( str_contains( $key, '::' ) && str_contains( $key, '.' ) ) || str_contains( $key, ' ' ) ) { $stringKeys[] = $key; } } } } // Remove duplicates $groupKeys = array_unique( $groupKeys ); $stringKeys = array_unique( $stringKeys ); // Add the translations to the database, if not existing. foreach ( $groupKeys as $key ) { // Split the group and item list( $group, $item ) = explode( '.', $key, 2 ); $this->missingKey( '', $group, $item ); } foreach ( $stringKeys as $key ) { $group = self::JSON_GROUP; $item = $key; $this->missingKey( '', $group, $item ); } // Return the number of found translations return count( $groupKeys + $stringKeys ); } public function missingKey( $namespace, $group, $key ) { if ( !in_array( $group, $this->config[ 'exclude_groups' ] ) ) { Translation::firstOrCreate( [ 'locale' => $this->app[ 'config' ][ 'app.locale' ], 'group' => $group, 'key' => $key, ] ); } } public function exportTranslations( $group = null, $json = false ) { $basePath = $this->app[ 'path.lang' ]; if ( !is_null( $group ) && !$json ) { if ( !in_array( $group, $this->config[ 'exclude_groups' ] ) ) { $vendor = false; if ( $group == '*' ) { return $this->exportAllTranslations(); } else { if ( starts_with( $group, "vendor" ) ) { $vendor = true; } } $tree = $this->makeTree( Translation::ofTranslatedGroup( $group ) ->orderByGroupKeys( array_get( $this->config, 'sort_keys', false ) ) ->get() ); foreach ( $tree as $locale => $groups ) { if ( isset( $groups[ $group ] ) ) { $translations = $groups[ $group ]; $path = $this->app[ 'path.lang' ]; $locale_path = $locale . DIRECTORY_SEPARATOR . $group; if ( $vendor ) { $path = $basePath . '/' . $group . '/' . $locale; $locale_path = str_after( $group, "/" ); } $subfolders = explode( DIRECTORY_SEPARATOR, $locale_path ); array_pop( $subfolders ); $subfolder_level = ''; foreach ( $subfolders as $subfolder ) { $subfolder_level = $subfolder_level . $subfolder . DIRECTORY_SEPARATOR; $temp_path = rtrim( $path . DIRECTORY_SEPARATOR . $subfolder_level, DIRECTORY_SEPARATOR ); if ( !is_dir( $temp_path ) ) { mkdir( $temp_path, 0777, true ); } } $path = $path . DIRECTORY_SEPARATOR . $locale . DIRECTORY_SEPARATOR . $group . '.php'; $output = "files->put( $path, $output ); } } Translation::ofTranslatedGroup( $group )->update( [ 'status' => Translation::STATUS_SAVED ] ); } } if ( $json ) { $tree = $this->makeTree( Translation::ofTranslatedGroup( self::JSON_GROUP ) ->orderByGroupKeys( array_get( $this->config, 'sort_keys', false ) ) ->get(), true ); foreach ( $tree as $locale => $groups ) { if ( isset( $groups[ self::JSON_GROUP ] ) ) { $translations = $groups[ self::JSON_GROUP ]; $path = $this->app[ 'path.lang' ] . '/' . $locale . '.json'; $output = json_encode( $translations, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_UNICODE ); $this->files->put( $path, $output ); } } Translation::ofTranslatedGroup( self::JSON_GROUP )->update( [ 'status' => Translation::STATUS_SAVED ] ); } $this->events->dispatch( new TranslationsExportedEvent() ); } public function exportAllTranslations() { $groups = Translation::whereNotNull( 'value' )->selectDistinctGroup()->get( 'group' ); foreach ( $groups as $group ) { if ( $group->group == self::JSON_GROUP ) { $this->exportTranslations( null, true ); } else { $this->exportTranslations( $group->group ); } } $this->events->dispatch( new TranslationsExportedEvent() ); } protected function makeTree( $translations, $json = false ) { $array = []; foreach ( $translations as $translation ) { if ( $json ) { $this->jsonSet( $array[ $translation->locale ][ $translation->group ], $translation->key, $translation->value ); } else { array_set( $array[ $translation->locale ][ $translation->group ], $translation->key, $translation->value ); } } return $array; } public function jsonSet( &$array, $key, $value ) { if ( is_null( $key ) ) { return $array = $value; } $array[ $key ] = $value; return $array; } public function cleanTranslations() { Translation::whereNull( 'value' )->delete(); } public function truncateTranslations() { Translation::truncate(); } public function getLocales() { if ( empty( $this->locales ) ) { $locales = array_merge( [ config( 'app.locale' ) ], Translation::groupBy( 'locale' )->pluck( 'locale' )->toArray() ); foreach ( $this->files->directories( $this->app->langPath() ) as $localeDir ) { if ( ( $name = $this->files->name( $localeDir ) ) != 'vendor' ) { $locales[] = $name; } } $this->locales = array_unique( $locales ); sort( $this->locales ); } return array_diff( $this->locales, $this->ignoreLocales ); } public function addLocale( $locale ) { $localeDir = $this->app->langPath() . '/' . $locale; $this->ignoreLocales = array_diff( $this->ignoreLocales, [ $locale ] ); $this->saveIgnoredLocales(); $this->ignoreLocales = $this->getIgnoredLocales(); if ( !$this->files->exists( $localeDir ) || !$this->files->isDirectory( $localeDir ) ) { return $this->files->makeDirectory( $localeDir ); } return true; } protected function saveIgnoredLocales() { return $this->files->put( $this->ignoreFilePath, json_encode( $this->ignoreLocales ) ); } public function removeLocale( $locale ) { if ( !$locale ) { return false; } $this->ignoreLocales = array_merge( $this->ignoreLocales, [ $locale ] ); $this->saveIgnoredLocales(); $this->ignoreLocales = $this->getIgnoredLocales(); Translation::where( 'locale', $locale )->delete(); } public function getConfig( $key = null ) { if ( $key == null ) { return $this->config; } else { return $this->config[ $key ]; } } }