├── .gitignore ├── src ├── Commands │ ├── bundle-file-stub.php │ ├── MakeBundlesFolder.php │ ├── LaravelLangBundlerCommand.php │ ├── MakeNewBundleFile.php │ └── MakeBundleMod.php ├── BundleItems │ ├── Mods │ │ ├── UcfirstMod.php │ │ ├── ChangeMod.php │ │ ├── StrtolowerMod.php │ │ ├── StrtoupperMod.php │ │ ├── ExplodeMod.php │ │ ├── ValuesMod.php │ │ └── CallbackMod.php │ ├── ModStub.php │ ├── ItemFactory.php │ ├── ItemWrapper.php │ └── BundleItem.php ├── Exceptions │ └── InvalidModificationArgument.php ├── helpers.php ├── LangBundler.php ├── Translator.php ├── config.php ├── ServiceProvider.php └── Bundle │ ├── BundleMap.php │ └── Bundle.php ├── .travis.yml ├── tests ├── stubs │ ├── en │ │ ├── nav.php │ │ ├── translations.php │ │ ├── user.php │ │ └── home.php │ ├── ja │ │ ├── nav.php │ │ └── home.php │ ├── bin2hex.php │ ├── bundle5.php │ ├── bundle6.php │ ├── bundle7.php │ ├── bundle9.php │ ├── bundle8.php │ ├── bundle1.php │ ├── components │ │ ├── sub-components │ │ │ └── bundle4.php │ │ └── bundle3.php │ ├── bundle2.php │ ├── bundle10.php │ ├── Bin2hexMod.php │ └── ExpectedResults.php ├── Unit │ ├── BundleTest.php │ ├── CommandsTest.php │ ├── ItemFactoryTest.php │ ├── BundleItemTest.php │ ├── BundleItemModTest.php │ └── BundleMapTest.php ├── Integration │ ├── IntegrationTest.php │ └── TranslatorTest.php └── TestCase.php ├── .scrutinizer.yaml ├── phpunit.xml ├── composer.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | .env 3 | composer.lock 4 | -------------------------------------------------------------------------------- /src/Commands/bundle-file-stub.php: -------------------------------------------------------------------------------- 1 | 'Home', 5 | 'top' => 'Top', 6 | ]; 7 | -------------------------------------------------------------------------------- /tests/stubs/ja/nav.php: -------------------------------------------------------------------------------- 1 | 'ホーム', 5 | 'top' => 'トップ', 6 | ]; 7 | -------------------------------------------------------------------------------- /tests/stubs/bin2hex.php: -------------------------------------------------------------------------------- 1 | 'ようこそ', 5 | 'signup' => 'サインアップ', 6 | 'login' => 'ログイン', 7 | ]; 8 | -------------------------------------------------------------------------------- /tests/stubs/bundle7.php: -------------------------------------------------------------------------------- 1 | 'Welcome', 5 | 'signup' => 'Signup', 6 | 'login' => 'Login', 7 | 'home' => 'Home', 8 | 'top' => 'Top', 9 | ]; 10 | -------------------------------------------------------------------------------- /tests/stubs/en/user.php: -------------------------------------------------------------------------------- 1 | 'Welcome :user', 5 | 'message_from' => 'You have a message from :sender', 6 | 'welcomeUser' => 'Welcome :user', 7 | 'messageFrom' => 'You have a message from :sender', 8 | 'message_to' => 'You sent a message to :user', 9 | 'invite_from' => 'You have an invite from :user', 10 | ]; 11 | -------------------------------------------------------------------------------- /tests/stubs/bundle2.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'home.welcome', 6 | 'home.signup', 7 | 'home.login', 8 | 'nav.home', 9 | 'nav.top', 10 | ], 11 | 12 | 'component2' => [ 13 | 'home.welcome', 14 | 'home.signup', 15 | 'home.login', 16 | 'nav.home', 17 | 'nav.top', 18 | ], 19 | ]; 20 | -------------------------------------------------------------------------------- /tests/stubs/components/bundle3.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'home.welcome', 6 | 'home.signup', 7 | 'home.login', 8 | 'nav.home', 9 | 'nav.top', 10 | ], 11 | 12 | 'forum' => [ 13 | 'component2' => [ 14 | 'home.welcome', 15 | 'home.signup', 16 | 'home.login', 17 | 'nav.home', 18 | 'nav.top', 19 | ], 20 | 21 | 'component3' => [ 22 | 'payment.creditcard', 23 | 'payment.date', 24 | 'payment.submit', 25 | ], 26 | ], 27 | ]; 28 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests/ 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/BundleItems/Mods/UcfirstMod.php: -------------------------------------------------------------------------------- 1 | wrapperParameters['new']; 19 | } 20 | 21 | /** 22 | * Alter value and return. 23 | * 24 | * @param mixed $value 25 | * 26 | * @return mixed 27 | */ 28 | public function value($value) 29 | { 30 | return $value; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/BundleItems/Mods/StrtolowerMod.php: -------------------------------------------------------------------------------- 1 | wrapperParameters['delimiter'], $value); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/BundleItems/Mods/ValuesMod.php: -------------------------------------------------------------------------------- 1 | 'Welcome', 5 | 'signup' => 'Signup', 6 | 'login' => 'Login', 7 | 'months' => [ 8 | 'january' => 'Jan', 9 | 'february' => 'Feb', 10 | 'march' => 'March', 11 | 'april' => 'April', 12 | 'may' => 'May', 13 | 'june' => 'June', 14 | 'july' => 'July', 15 | 'august' => 'Aug', 16 | 'september' => 'Sept', 17 | 'october' => 'Oct', 18 | 'november' => 'Nov', 19 | 'december' => 'Dec', 20 | ], 21 | 'inbox_status' => 'You have a new message.|You have new messages', 22 | 'lowercase' => 'lowercase string', 23 | 'uppercase' => 'UPPERSACE STRING', 24 | ]; 25 | -------------------------------------------------------------------------------- /src/Exceptions/InvalidModificationArgument.php: -------------------------------------------------------------------------------- 1 | 'ucfirst', 6 | ]), 7 | bundle_item('user.message_to', 'value_callback', [ 8 | 'callback' => 'strtoupper', 9 | ]), 10 | bundle_item('user.invite_from', 'key_callback', [ 11 | 'callback' => 'studly_case', 12 | ]), 13 | bundle_item('user.invite_from', 'key_change', [ 14 | 'new' => 'newKey', 15 | ]), 16 | bundle_item('home.months', 'value_values'), 17 | bundle_item('user.message_to', 'both_callback', [ 18 | 'callback' => 'strtoupper', 19 | ]), 20 | bundle_item('home.lowercase', 'value_ucfirst'), 21 | bundle_item('home.lowercase', 'value_strtoupper'), 22 | bundle_item('home.lowercase', 'value_explode', [ 23 | 'delimiter' => ' ', 24 | ]), 25 | bundle_item('home.uppercase', 'value_strtolower'), 26 | ]; 27 | -------------------------------------------------------------------------------- /src/Commands/MakeBundlesFolder.php: -------------------------------------------------------------------------------- 1 | setUp(); 27 | 28 | $directory = resource_path('lang'.DIRECTORY_SEPARATOR.'bundles'); 29 | 30 | if (!$this->filesystem->exists($directory)) { 31 | $this->filesystem->makeDirectory($directory); 32 | } 33 | 34 | $this->info('Bundles folder successfully created!'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/BundleItems/Mods/CallbackMod.php: -------------------------------------------------------------------------------- 1 | callCallback($key); 19 | } 20 | 21 | /** 22 | * Alter value and return. 23 | * 24 | * @param string $value 25 | * 26 | * @return mixed 27 | */ 28 | public function value($value) 29 | { 30 | return $this->callCallback($value); 31 | } 32 | 33 | /** 34 | * Call the given callback. 35 | * 36 | * @param string $string 37 | * 38 | * @return string 39 | */ 40 | protected function callCallback($string) 41 | { 42 | if (is_callable($this->wrapperParameters['callback'])) { 43 | $callback = $this->wrapperParameters['callback']; 44 | 45 | return $callback($string); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Commands/LaravelLangBundlerCommand.php: -------------------------------------------------------------------------------- 1 | filesystem = new Filesystem(); 23 | } 24 | 25 | /** 26 | * Build path from path array. 27 | * 28 | * @param array $pathArray 29 | * @param string $path 30 | * 31 | * @return string 32 | */ 33 | protected function buildPath($pathArray, $path) 34 | { 35 | foreach ($pathArray as $directory) { 36 | $path .= $directory.DIRECTORY_SEPARATOR; 37 | 38 | if (!$this->filesystem->exists($path)) { 39 | $this->filesystem->makeDirectory($path); 40 | } 41 | } 42 | 43 | return $path; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zachleigh/laravel-lang-bundler", 3 | "description": "Create Laravel translations bundles.", 4 | "keywords": ["laravel", "localization", "translation", "language", "lang"], 5 | "type": "project", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Zach Leigh", 10 | "email": "zachleigh@fastmail.jp" 11 | } 12 | ], 13 | 14 | "require": { 15 | "php": ">=7.1", 16 | "laravel/browser-kit-testing": "^1.0" 17 | }, 18 | 19 | "require-dev": { 20 | "laravel/laravel": "5.7.*", 21 | "phpunit/phpunit": "~4.0" 22 | }, 23 | 24 | "autoload": { 25 | "psr-4": { 26 | "LaravelLangBundler\\": "src/" 27 | }, 28 | 29 | "files": [ 30 | "src/helpers.php" 31 | ] 32 | }, 33 | 34 | "autoload-dev": { 35 | "psr-4": { 36 | "LaravelLangBundler\\tests\\": "tests/" 37 | } 38 | }, 39 | 40 | "scripts": { 41 | "test": "phpunit" 42 | }, 43 | 44 | "minimum-stability": "stable", 45 | "prefer-stable": true 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Zach Leigh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/BundleItems/ModStub.php: -------------------------------------------------------------------------------- 1 | copyStubs('bundle2'); 20 | 21 | $bundle = new Bundle('bundles.bundle2.component2', $this->bundleMap); 22 | 23 | foreach ($bundle->getValues() as $value) { 24 | $this->assertInstanceOf(BundleItem::class, $value); 25 | } 26 | } 27 | 28 | /** 29 | * @test 30 | */ 31 | public function if_value_is_already_a_translation_object_it_passes() 32 | { 33 | $this->copyStubs('bundle9'); 34 | 35 | $bundle = new Bundle('bundle9', $this->bundleMap); 36 | 37 | foreach ($bundle->getValues() as $value) { 38 | $this->assertInstanceOf(BundleItem::class, $value); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/helpers.php: -------------------------------------------------------------------------------- 1 | trans($id, $parameters, $locale); 22 | } 23 | } 24 | 25 | if (!function_exists('bundle_item')) { 26 | /** 27 | * Create a BundleItem class directly from the bundle registration file. 28 | * 29 | * @param string $id 30 | * @param string $type target_modType 31 | * @param array $parameters 32 | * 33 | * @return BundleItem 34 | */ 35 | function bundle_item($id, $type = null, array $parameters = []) 36 | { 37 | return ItemFactory::build($id, $type, $parameters); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/stubs/Bin2hexMod.php: -------------------------------------------------------------------------------- 1 | bundleMap = $bundleMap; 30 | $this->translator = $translator; 31 | 32 | $this->bundleMap->mapBundles(); 33 | } 34 | 35 | /** 36 | * Translate the given message. 37 | * 38 | * @param string $id 39 | * @param array $parameters 40 | * @param string $locale 41 | * 42 | * @return \Illuminate\Support\Collection 43 | */ 44 | public function trans($id, array $parameters = [], $locale = null) 45 | { 46 | $bundle = new Bundle($id, $this->bundleMap); 47 | 48 | if ($bundle->hasNoValues()) { 49 | return app('translator')->trans($id, $parameters, $locale); 50 | } 51 | 52 | return $this->translator->translateBundle($bundle, $parameters, $locale); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Translator.php: -------------------------------------------------------------------------------- 1 | getValues() 26 | ->mapWithKeys(function (BundleItem $bundleItem) use ($parameters, $locale) { 27 | $bundleItem->setParameters($parameters); 28 | 29 | if ($choice = $bundleItem->hasChoice()) { 30 | $value = app('translator')->transChoice( 31 | $bundleItem->getId(), 32 | $choice, 33 | $bundleItem->getParameters(), 34 | $locale 35 | ); 36 | } else { 37 | $value = app('translator')->trans( 38 | $bundleItem->getId(), 39 | $bundleItem->getParameters(), 40 | $locale 41 | ); 42 | } 43 | 44 | return $bundleItem->setValue($value)->getReturnArray(); 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Commands/MakeNewBundleFile.php: -------------------------------------------------------------------------------- 1 | setUp(); 27 | 28 | $pathArray = collect(explode('.', $this->argument('path'))); 29 | 30 | $filename = $pathArray->pop().'.php'; 31 | 32 | $pathArray->prepend('bundles'); 33 | 34 | $basePath = resource_path('lang'.DIRECTORY_SEPARATOR); 35 | 36 | $path = $this->buildPath($pathArray->all(), $basePath); 37 | 38 | $this->createFile($path, $filename); 39 | 40 | $this->info('Bundle file successfully created!'); 41 | } 42 | 43 | /** 44 | * Create file from stub. 45 | * 46 | * @param string $path 47 | * @param string $filename 48 | */ 49 | protected function createFile($path, $filename) 50 | { 51 | $filePath = $path.$filename; 52 | 53 | $stub = __DIR__.DIRECTORY_SEPARATOR.'bundle-file-stub.php'; 54 | 55 | if (!$this->filesystem->exists($filePath)) { 56 | $this->filesystem->copy($stub, $filePath); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/config.php: -------------------------------------------------------------------------------- 1 | [ 15 | // 'alias' => 'path.to.bundle' 16 | ], 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Transform Lang Keys 21 | |-------------------------------------------------------------------------- 22 | | 23 | | Transform lang value keys. 24 | | Available options: 'camel_case', 'none', 'snake_case', 'studly_case'. 25 | */ 26 | 'key_transform' => 'none', 27 | 28 | /* 29 | |-------------------------------------------------------------------------- 30 | | Global Key Namespace 31 | |-------------------------------------------------------------------------- 32 | | 33 | | If you keep your translations in a single file, it may be useful to use a 34 | | global key namespace. For example, if your translations are in a file 35 | | called 'translations', for every key you have to type 'translations.key'. 36 | | Set the namespace to 'translations' so you only have to type 'key'. 37 | */ 38 | 'global_key_namespace' => '', 39 | ]; 40 | -------------------------------------------------------------------------------- /src/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton(LangBundler::class, function () { 21 | return new LangBundler( 22 | new BundleMap(), 23 | new Translator() 24 | ); 25 | }); 26 | 27 | $this->registerCommands(); 28 | } 29 | 30 | /** 31 | * Perform post-registration booting of services. 32 | * 33 | * @return void 34 | */ 35 | public function boot() 36 | { 37 | $this->publishes([ 38 | __DIR__.DIRECTORY_SEPARATOR.'config.php' => config_path('lang-bundler.php'), 39 | ], 'config'); 40 | } 41 | 42 | /** 43 | * Register Artisan commands. 44 | */ 45 | protected function registerCommands() 46 | { 47 | $this->app->singleton('command.langb.start', function ($app) { 48 | return $app[MakeBundlesFolder::class]; 49 | }); 50 | 51 | $this->app->singleton('command.langb.new', function ($app) { 52 | return $app[MakeNewBundleFile::class]; 53 | }); 54 | 55 | $this->app->singleton('command.langb.mod', function ($app) { 56 | return $app[MakeBundleMod::class]; 57 | }); 58 | 59 | $this->commands('command.langb.start'); 60 | 61 | $this->commands('command.langb.new'); 62 | 63 | $this->commands('command.langb.mod'); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/Unit/CommandsTest.php: -------------------------------------------------------------------------------- 1 | assertFileNotExists($directory); 17 | 18 | Artisan::call('langb:start'); 19 | 20 | $this->assertFileExists($directory); 21 | } 22 | 23 | /** 24 | * @test 25 | */ 26 | public function make_new_bundle_file_makes_new_file() 27 | { 28 | $file = resource_path('lang/bundles/test/components/new.php'); 29 | 30 | $this->assertFileNotExists($file); 31 | 32 | Artisan::call('langb:new', ['path' => 'test.components.new']); 33 | 34 | $this->assertFileExists($file); 35 | } 36 | 37 | /** 38 | * @test 39 | */ 40 | public function make_new_bundle_file_doesnt_overwrite_folders() 41 | { 42 | Artisan::call('langb:new', ['path' => 'test.new']); 43 | 44 | Artisan::call('langb:new', ['path' => 'test.components.new']); 45 | 46 | Artisan::call('langb:new', ['path' => 'test.newer']); 47 | 48 | Artisan::call('langb:new', ['path' => 'new']); 49 | 50 | $files = [ 51 | resource_path('lang/bundles/test/new.php'), 52 | resource_path('lang/bundles/test/components/new.php'), 53 | resource_path('lang/bundles/test/newer.php'), 54 | resource_path('lang/bundles/new.php'), 55 | ]; 56 | 57 | foreach ($files as $file) { 58 | $this->assertFileExists($file); 59 | } 60 | } 61 | 62 | /** 63 | * @test 64 | */ 65 | public function make_bundle_wrapper_makes_a_bundle_wrapper() 66 | { 67 | Artisan::call('langb:mod', ['name' => 'test']); 68 | 69 | $this->assertFileExists(app_path('LangBundler/Mods/TestMod.php')); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/BundleItems/ItemFactory.php: -------------------------------------------------------------------------------- 1 | getNamespace(); 43 | 44 | $localClass = "\\{$appNamespace}LangBundler\\Mods\\{$className}"; 45 | 46 | $vendorClass = "\LaravelLangBundler\\BundleItems\\Mods\\{$className}"; 47 | 48 | if (class_exists($localClass)) { 49 | return new $localClass($id, $target, $parameters); 50 | } elseif (class_exists($vendorClass)) { 51 | return new $vendorClass($id, $target, $parameters); 52 | } 53 | 54 | throw InvalidModificationArgument::modifcationClassNotFound($className); 55 | } 56 | 57 | /** 58 | * Validate the target. 59 | * 60 | * @param string $target 61 | * 62 | * @throws InvalidModificationTarget 63 | */ 64 | protected static function validateTarget($target) 65 | { 66 | if (!in_array($target, self::ALLOWEDTARGETS)) { 67 | throw InvalidModificationArgument::targetNotAllowed($target); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Commands/MakeBundleMod.php: -------------------------------------------------------------------------------- 1 | setUp(); 29 | 30 | $pathArray = ['LangBundler', 'Mods']; 31 | 32 | $basePath = app_path().DIRECTORY_SEPARATOR; 33 | 34 | $this->buildPath($pathArray, $basePath); 35 | 36 | $name = ucfirst($this->argument('name')).'Mod'; 37 | 38 | $stub = $this->getStub($name); 39 | 40 | $this->filesystem->put( 41 | app_path(implode(DIRECTORY_SEPARATOR, $pathArray).DIRECTORY_SEPARATOR.$name.'.php'), 42 | $stub 43 | ); 44 | 45 | $this->info('New bundle modification file successfully created!'); 46 | } 47 | 48 | /** 49 | * Get stub and fill. 50 | * 51 | * @param string $name 52 | * 53 | * @return string 54 | */ 55 | protected function getStub($name) 56 | { 57 | $namespace = Container::getInstance()->getNamespace(); 58 | 59 | $namespace = $namespace.'LangBundler\\Mods'; 60 | 61 | $stub = $this->filesystem->get( 62 | dirname(__DIR__).DIRECTORY_SEPARATOR.'BundleItems'.DIRECTORY_SEPARATOR.'ModStub.php'); 63 | 64 | $stub = str_replace( 65 | 'LaravelLangBundler\BundleItems', 66 | $namespace, 67 | $stub 68 | ); 69 | 70 | $stub = str_replace( 71 | 'ModStub', 72 | $name, 73 | $stub 74 | ); 75 | 76 | return $stub; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/BundleItems/ItemWrapper.php: -------------------------------------------------------------------------------- 1 | target = $target; 33 | 34 | $this->wrapperParameters = $wrapperParameters; 35 | } 36 | 37 | /** 38 | * Get the set target. 39 | * 40 | * @return string 41 | */ 42 | public function getAffect() 43 | { 44 | return $this->target; 45 | } 46 | 47 | /** 48 | * If target is key, get key from child. Otherwise, get from parent. 49 | * 50 | * @return string 51 | */ 52 | public function getKey() 53 | { 54 | if ($this->target === 'key' || $this->target === 'both') { 55 | return $this->key($this->key); 56 | } 57 | 58 | return parent::getKey(); 59 | } 60 | 61 | /** 62 | * If target is value, get value from child. Otherwise, get from parent. 63 | * 64 | * @return mixed 65 | */ 66 | public function getValue() 67 | { 68 | if ($this->target === 'value' || $this->target === 'both') { 69 | return $this->value($this->value); 70 | } 71 | 72 | return parent::getValue(); 73 | } 74 | 75 | /** 76 | * Alter key and return. 77 | * 78 | * @param string $key 79 | * 80 | * @return string 81 | */ 82 | abstract public function key($key); 83 | 84 | /** 85 | * Alter value and return. 86 | * 87 | * @param mixed $value 88 | * 89 | * @return mixed 90 | */ 91 | abstract public function value($value); 92 | } 93 | -------------------------------------------------------------------------------- /tests/Unit/ItemFactoryTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf(BundleItem::class, $bundleItem); 23 | } 24 | 25 | /** 26 | * @test 27 | */ 28 | public function it_makes_mod_item_with_value() 29 | { 30 | $bundleItem = ItemFactory::build('id', 'value_strtoupper'); 31 | 32 | $this->assertInstanceOf(StrtoupperMod::class, $bundleItem); 33 | 34 | $this->assertEquals('value', $bundleItem->getAffect()); 35 | } 36 | 37 | /** 38 | * @test 39 | */ 40 | public function it_makes_mod_item_with_key() 41 | { 42 | $bundleItem = ItemFactory::build('id', 'key_strtoupper'); 43 | 44 | $this->assertInstanceOf(StrtoupperMod::class, $bundleItem); 45 | 46 | $this->assertEquals('key', $bundleItem->getAffect()); 47 | } 48 | 49 | /** 50 | * @test 51 | */ 52 | public function it_makes_mod_item_with_both() 53 | { 54 | $bundleItem = ItemFactory::build('id', 'both_strtoupper'); 55 | 56 | $this->assertInstanceOf(StrtoupperMod::class, $bundleItem); 57 | 58 | $this->assertEquals('both', $bundleItem->getAffect()); 59 | } 60 | 61 | /** 62 | * @test 63 | * 64 | * @expectedException LaravelLangBundler\Exceptions\InvalidModificationArgument 65 | * @expectedExceptionMessage Target invalid is not allowed. Allowed targets are 'key', 'value', and 'both'. 66 | */ 67 | public function it_throws_exception_for_invalid_target_value() 68 | { 69 | $bundleItem = ItemFactory::build('id', 'invalid_strtoupper'); 70 | } 71 | 72 | /** 73 | * @test 74 | * 75 | * @expectedException LaravelLangBundler\Exceptions\InvalidModificationArgument 76 | * @expectedExceptionMessage Class InvalidMod can not be found. 77 | */ 78 | public function it_throws_exception_for_invalid_mod_name() 79 | { 80 | $bundleItem = ItemFactory::build('id', 'key_invalid'); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /tests/Unit/BundleItemTest.php: -------------------------------------------------------------------------------- 1 | copyStubs('bundle7'); 19 | 20 | $this->copyTranslations(); 21 | 22 | app()['config']->set('lang-bundler.global_key_namespace', 'translations'); 23 | 24 | $bundle = new Bundle('bundles.bundle7', $this->bundleMap); 25 | 26 | foreach ($bundle->getValues() as $item) { 27 | $expected = 'translations.'.$item->getNamespace(); 28 | 29 | $this->assertEquals($expected, $item->getId()); 30 | } 31 | } 32 | 33 | /** 34 | * @test 35 | */ 36 | public function it_transforms_keys() 37 | { 38 | $this->copyStubs('bundle5'); 39 | 40 | $this->copyTranslations(); 41 | 42 | app()['config']->set('lang-bundler.key_transform', 'studly_case'); 43 | 44 | $bundle = new Bundle('bundles.bundle5', $this->bundleMap); 45 | 46 | foreach ($bundle->getValues() as $item) { 47 | $expected = studly_case($item->getNamespace()); 48 | 49 | $this->assertEquals($expected, $item->getKey()); 50 | } 51 | } 52 | 53 | /** 54 | * @test 55 | */ 56 | public function it_ignores_parameters_with_different_namespaces() 57 | { 58 | $this->copyStubs('bundle8'); 59 | 60 | $this->copyTranslations(); 61 | 62 | $bundle = new Bundle('bundles.bundle8', $this->bundleMap); 63 | 64 | foreach ($bundle->getValues() as $item) { 65 | $parameters = [ 66 | 'welcome_user.user' => 'Bob', 67 | 'message_to.user' => 'Sally', 68 | 'invite_from.user' => 'George', 69 | ]; 70 | 71 | $item->setParameters($parameters); 72 | 73 | if (isset($parameters[$item->getKey().'.user'])) { 74 | $expected = $parameters[$item->getKey().'.user']; 75 | 76 | $this->assertEquals($expected, $item->getParameters()['user']); 77 | } 78 | } 79 | } 80 | 81 | /** 82 | * @test 83 | */ 84 | public function it_sets_choice_if_namespaced_choice_value_passed_in_parameters() 85 | { 86 | $this->copyStubs('bundle8'); 87 | 88 | $this->copyTranslations(); 89 | 90 | $bundle = new Bundle('bundles.bundle8', $this->bundleMap); 91 | 92 | $item = $bundle->getValues()[3]; 93 | 94 | $item->setParameters([ 95 | 'inbox_status.choice' => 3, 96 | ]); 97 | 98 | $this->assertEquals(3, $item->hasChoice()); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tests/Integration/IntegrationTest.php: -------------------------------------------------------------------------------- 1 | copyStubs('bundle2'); 19 | 20 | $this->copyTranslations(); 21 | 22 | $translations = app(LangBundler::class)->trans('bundles.bundle2.component1'); 23 | 24 | $expected = $this->getExpected(['homeEn', 'navEn']); 25 | 26 | $this->assertEquals($expected, $translations->all()); 27 | } 28 | 29 | /** 30 | * @test 31 | */ 32 | public function bundles_can_be_accessed_from_helper() 33 | { 34 | $this->copyStubs('bundle2'); 35 | 36 | $this->copyTranslations(); 37 | 38 | $translations = transB('bundles.bundle2.component1'); 39 | 40 | $expected = $this->getExpected(['homeEn', 'navEn']); 41 | 42 | $this->assertEquals($expected, $translations->all()); 43 | } 44 | 45 | /** 46 | * @test 47 | */ 48 | public function helper_function_with_no_arguments_returns_lang_bundler_class() 49 | { 50 | $langBundler = transB(); 51 | 52 | $this->assertInstanceOf('LaravelLangBundler\LangBundler', $langBundler); 53 | } 54 | 55 | /** 56 | * @test 57 | */ 58 | public function passing_a_normal_lang_value_to_helper_function_returns_the_translation() 59 | { 60 | $this->copyTranslations(); 61 | 62 | $translation = transB('home.welcome'); 63 | 64 | $this->assertEquals('Welcome', $translation); 65 | } 66 | 67 | /** 68 | * @test 69 | */ 70 | public function parameters_can_be_sent_through_app_instance() 71 | { 72 | $this->copyStubs('bundle5'); 73 | 74 | $this->copyTranslations(); 75 | 76 | $translations = app(LangBundler::class)->trans( 77 | 'bundles.bundle5', 78 | ['user' => 'Bob', 'sender' => 'Sally'] 79 | ); 80 | 81 | $expected = [ 82 | 'welcome_user' => 'Welcome Bob', 83 | 'message_from' => 'You have a message from Sally', 84 | ]; 85 | 86 | $this->assertEquals($expected, $translations->all()); 87 | } 88 | 89 | /** 90 | * @test 91 | */ 92 | public function helper_function_accepts_parameters() 93 | { 94 | $this->copyStubs('bundle5'); 95 | 96 | $this->copyTranslations(); 97 | 98 | $translations = transB('bundles.bundle5', ['user' => 'Bob', 'sender' => 'Sally']); 99 | 100 | $expected = [ 101 | 'welcome_user' => 'Welcome Bob', 102 | 'message_from' => 'You have a message from Sally', 103 | ]; 104 | 105 | $this->assertEquals($expected, $translations->all()); 106 | } 107 | 108 | /** 109 | * @test 110 | */ 111 | public function helper_function_works_with_bundle_item_wrapper() 112 | { 113 | $this->copyStubs('bundle10'); 114 | 115 | $this->copyTranslations(); 116 | 117 | $translations = transB('bundle10')->take(4); 118 | 119 | $expected = $this->getExpected('bundle10'); 120 | 121 | $this->assertEquals($expected, $translations->all()); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | register(ServiceProvider::class); 38 | 39 | $app->make(Kernel::class)->bootstrap(); 40 | 41 | return $app; 42 | } 43 | 44 | /** 45 | * Setup DB and test variables before each test. 46 | */ 47 | protected function setUp() 48 | { 49 | $filesystem = new Filesystem(); 50 | 51 | $configDir = __DIR__.'./../src/config.php'; 52 | 53 | $configTarget = __DIR__.'./../vendor/laravel/laravel/config/lang-bundler.php'; 54 | 55 | $filesystem->copy($configDir, $configTarget); 56 | 57 | parent::setUp(); 58 | 59 | $this->bundleMap = new BundleMap(); 60 | 61 | $this->translator = new Translator(); 62 | 63 | if (!file_exists(resource_path('lang'))) { 64 | $filesystem->makeDirectory(resource_path('lang')); 65 | } 66 | } 67 | 68 | /** 69 | * Teardown after each class. 70 | */ 71 | protected function tearDown() 72 | { 73 | $filesystem = new Filesystem(); 74 | 75 | $filesystem->deleteDirectory(resource_path('lang')); 76 | 77 | $filesystem->delete(config_path('lang-bundler.php')); 78 | 79 | $filesystem->deleteDirectory(app_path('LangBundler')); 80 | 81 | parent::tearDown(); 82 | } 83 | 84 | /** 85 | * Copy stubs to vendor Laravel app lang directory. 86 | * 87 | * @param string|array $stubs 88 | */ 89 | protected function copyStubs($stubs) 90 | { 91 | if (!is_array($stubs)) { 92 | $stubs = [$stubs]; 93 | } 94 | 95 | $filesystem = new Filesystem(); 96 | 97 | $filesystem->makeDirectory(resource_path('lang/bundles/')); 98 | 99 | foreach ($stubs as $stub) { 100 | $stubPath = __DIR__.'/stubs/'.$stub; 101 | 102 | if (is_dir($stubPath)) { 103 | $filesystem->copyDirectory( 104 | $stubPath, 105 | resource_path('lang/bundles/'.$stub) 106 | ); 107 | } else { 108 | $target = resource_path('lang/bundles/'.$stub.'.php'); 109 | 110 | $stubPath = $stubPath.'.php'; 111 | 112 | if (file_exists($stubPath)) { 113 | $filesystem->copy($stubPath, $target); 114 | } 115 | } 116 | } 117 | } 118 | 119 | /** 120 | * Copy translation files into vendor Laravel app. 121 | */ 122 | protected function copyTranslations() 123 | { 124 | $filesystem = new Filesystem(); 125 | 126 | if (!file_exists(resource_path('lang/en/'))) { 127 | $filesystem->makeDirectory(resource_path('lang/en/')); 128 | } 129 | 130 | if (!file_exists(resource_path('lang/ja/'))) { 131 | $filesystem->makeDirectory(resource_path('lang/ja/')); 132 | } 133 | 134 | $translations = [ 135 | 'en' => __DIR__.'/stubs/en/', 136 | 'ja' => __DIR__.'/stubs/ja/', 137 | ]; 138 | 139 | foreach ($translations as $name => $translation) { 140 | $target = resource_path('lang/'.$name); 141 | 142 | $filesystem->copyDirectory($translation, $target); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/Bundle/BundleMap.php: -------------------------------------------------------------------------------- 1 | bundleMap; 31 | } 32 | 33 | /** 34 | * Get auto-alias array. 35 | * 36 | * @return array 37 | */ 38 | public function getAutoAliases() 39 | { 40 | return $this->autoAliases; 41 | } 42 | 43 | /** 44 | * Return true if bundleMap is empty. 45 | * 46 | * @return bool 47 | */ 48 | public function bundleMapIsEmpty() 49 | { 50 | return empty($this->bundleMap); 51 | } 52 | 53 | /** 54 | * Get trans values for path keys. 55 | * 56 | * @param array $pathKeys 57 | * 58 | * @return Collection 59 | */ 60 | public function getBundleValues(array $pathKeys) 61 | { 62 | $this->mapBundles(); 63 | 64 | $temp = &$this->bundleMap; 65 | 66 | foreach ($pathKeys as $key) { 67 | $temp = &$temp[$key]; 68 | } 69 | 70 | return collect($temp); 71 | } 72 | 73 | /** 74 | * Get all the lang bundle files from the bundles directory. 75 | * 76 | * @return array 77 | */ 78 | public function mapBundles() 79 | { 80 | if (!empty($this->bundleMap)) { 81 | return $this->getBundleMap(); 82 | } 83 | 84 | $pathCollection = $this->getPathCollection(); 85 | 86 | foreach ($pathCollection as $path) { 87 | $content = include $path; 88 | 89 | $pathKeys = $this->getPathKeys($path); 90 | 91 | $this->registerAlias(collect($pathKeys)); 92 | 93 | $this->mapContent($content, $pathKeys); 94 | } 95 | 96 | return $this->getBundleMap(); 97 | } 98 | 99 | /** 100 | * Get paths to all bundle files. 101 | * 102 | * @return Collection 103 | */ 104 | protected function getPathCollection() 105 | { 106 | $bundlePath = resource_path('lang/bundles'); 107 | 108 | if (!file_exists($bundlePath)) { 109 | return collect([]); 110 | } 111 | 112 | $directory = new \RecursiveDirectoryIterator($bundlePath); 113 | 114 | $iterator = new \RecursiveIteratorIterator($directory); 115 | 116 | $paths = new \RegexIterator( 117 | $iterator, 118 | '/^.+\.php$/i', 119 | \RecursiveRegexIterator::GET_MATCH 120 | ); 121 | 122 | return collect(iterator_to_array($paths))->flatten(); 123 | } 124 | 125 | /** 126 | * Get array of keys describing file path. 127 | * 128 | * @param string $path 129 | * 130 | * @return array 131 | */ 132 | protected function getPathKeys($path) 133 | { 134 | $key = str_replace('.php', '', explode(DIRECTORY_SEPARATOR.'bundles'.DIRECTORY_SEPARATOR, $path)[1]); 135 | 136 | return explode('/', $key); 137 | } 138 | 139 | /** 140 | * Register file paths as auto aliases. 141 | * 142 | * @param Collection $pathKeys 143 | */ 144 | protected function registerAlias(Collection $pathKeys) 145 | { 146 | $className = $pathKeys->last(); 147 | 148 | $path = $pathKeys->implode('.'); 149 | 150 | $this->autoAliases[$path] = $className; 151 | } 152 | 153 | /** 154 | * Map content on bundleMap. 155 | * 156 | * @param array $content 157 | * @param array $pathKeys 158 | */ 159 | protected function mapContent(array $content, array $pathKeys) 160 | { 161 | $temp = &$this->bundleMap; 162 | 163 | foreach ($pathKeys as $key) { 164 | $temp = &$temp[$key]; 165 | } 166 | 167 | $temp = $content; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /tests/Unit/BundleItemModTest.php: -------------------------------------------------------------------------------- 1 | copyStubs('bundle10'); 21 | 22 | $bundle = new Bundle('bundle10', $this->bundleMap); 23 | 24 | foreach ($bundle->getValues() as $key => $value) { 25 | if ($key > 2) { 26 | break; 27 | } 28 | 29 | $this->assertInstanceOf(CallbackMod::class, $value); 30 | } 31 | } 32 | 33 | /** 34 | * @test 35 | */ 36 | public function parameters_can_still_be_passed_to_transb() 37 | { 38 | extract($this->testMod(['user' => 'Bob'])); 39 | 40 | $this->assertEquals('Welcome Bob', $values[0]); 41 | 42 | $this->assertEquals('YOU SENT A MESSAGE TO BOB', $values[1]); 43 | 44 | $this->assertEquals('You have an invite from Bob', $values[2]); 45 | } 46 | 47 | /** 48 | * @test 49 | */ 50 | public function mod_callback_transforms_keys() 51 | { 52 | extract($this->testMod()); 53 | 54 | $this->assertEquals('Welcome_user', $keys[0]); 55 | 56 | $this->assertEquals('message_to', $keys[1]); 57 | 58 | $this->assertEquals('InviteFrom', $keys[2]); 59 | } 60 | 61 | /** 62 | * @test 63 | */ 64 | public function mod_callback_transforms_values() 65 | { 66 | extract($this->testMod()); 67 | 68 | $this->assertEquals('Welcome :user', $values[0]); 69 | 70 | $this->assertEquals('YOU SENT A MESSAGE TO :USER', $values[1]); 71 | 72 | $this->assertEquals('You have an invite from :user', $values[2]); 73 | } 74 | 75 | /** 76 | * @test 77 | */ 78 | public function mod_callback_transforms_both() 79 | { 80 | extract($this->testMod()); 81 | 82 | $this->assertEquals('MESSAGE_TO', $keys[5]); 83 | 84 | $this->assertEquals('YOU SENT A MESSAGE TO :USER', $values[5]); 85 | } 86 | 87 | /** 88 | * @test 89 | */ 90 | public function mods_can_be_read_from_app_directory() 91 | { 92 | Artisan::call('langb:mod', ['name' => 'test']); 93 | 94 | $path = app_path('LangBundler/Mods/Bin2hexMod.php'); 95 | 96 | $stub = file_get_contents(__DIR__.'/../stubs/Bin2hexMod.php'); 97 | 98 | file_put_contents($path, $stub); 99 | 100 | $this->copyStubs('bin2hex'); 101 | 102 | $this->copyTranslations(); 103 | 104 | $bundle = new Bundle('bin2hex', $this->bundleMap); 105 | 106 | $this->translator->translateBundle($bundle); 107 | 108 | $this->assertEquals( 109 | '77656c636f6d655f75736572', 110 | $bundle->getValues()[0]->getKey() 111 | ); 112 | } 113 | 114 | /** 115 | * @test 116 | */ 117 | public function mod_change_works() 118 | { 119 | extract($this->testMod()); 120 | 121 | $this->assertEquals('newKey', $keys[3]); 122 | } 123 | 124 | /** 125 | * @test 126 | */ 127 | public function mod_values_works() 128 | { 129 | extract($this->testMod()); 130 | 131 | $expected = $this->getExpected('months'); 132 | 133 | $this->assertEquals($expected, $values[4]); 134 | } 135 | 136 | /** 137 | * @test 138 | */ 139 | public function mod_ucfirst_works() 140 | { 141 | extract($this->testMod()); 142 | 143 | $this->assertEquals('Lowercase string', $values[6]); 144 | } 145 | 146 | /** 147 | * @test 148 | */ 149 | public function mod_strtoupper_works() 150 | { 151 | extract($this->testMod()); 152 | 153 | $this->assertEquals('LOWERCASE STRING', $values[7]); 154 | } 155 | 156 | /** 157 | * @test 158 | */ 159 | public function mod_explode_works() 160 | { 161 | extract($this->testMod()); 162 | 163 | $this->assertEquals(['lowercase', 'string'], $values[8]); 164 | } 165 | 166 | /** 167 | * @test 168 | */ 169 | public function mod_strtolower_works() 170 | { 171 | extract($this->testMod()); 172 | 173 | $this->assertEquals('uppersace string', $values[9]); 174 | } 175 | 176 | /** 177 | * Run the mod test and return keys and values. 178 | * 179 | * @return array 180 | */ 181 | public function testMod($parameters = []) 182 | { 183 | $this->copyStubs('bundle10'); 184 | 185 | $this->copyTranslations(); 186 | 187 | $bundle = new Bundle('bundle10', $this->bundleMap); 188 | 189 | $this->translator->translateBundle($bundle, $parameters); 190 | 191 | $keys = $bundle->getValues()->map(function ($value) { 192 | return $value->getKey(); 193 | }); 194 | 195 | $values = $bundle->getValues()->map(function ($value) { 196 | return $value->getValue(); 197 | }); 198 | 199 | return ['keys' => $keys, 'values' => $values]; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/Bundle/Bundle.php: -------------------------------------------------------------------------------- 1 | id = $id; 61 | $this->path = $id; 62 | 63 | $this->bundleMap = $bundleMap; 64 | 65 | $this->getValuesFromMap(); 66 | } 67 | 68 | /** 69 | * Get bundle namespace. 70 | * 71 | * @return string 72 | */ 73 | public function getNamespace() 74 | { 75 | return $this->namespace; 76 | } 77 | 78 | /** 79 | * Get pathKeys array. 80 | * 81 | * @return array 82 | */ 83 | public function getPathKeys() 84 | { 85 | return $this->pathKeys; 86 | } 87 | 88 | /** 89 | * Get path values collection. 90 | * 91 | * @return Collection 92 | */ 93 | public function getValues() 94 | { 95 | return $this->values; 96 | } 97 | 98 | /** 99 | * Return array of values. 100 | * 101 | * @return array 102 | */ 103 | public function getValuesArray() 104 | { 105 | return $this->values->all(); 106 | } 107 | 108 | /** 109 | * Return true if bundle contains no trans values. 110 | * 111 | * @return bool 112 | */ 113 | public function hasNoValues() 114 | { 115 | return $this->values->isEmpty(); 116 | } 117 | 118 | /** 119 | * Return true if namespace is valid bundle namespace. 120 | * 121 | * @return bool 122 | */ 123 | public function hasValidNamespace() 124 | { 125 | return $this->getNamespace() === 'bundles'; 126 | } 127 | 128 | /** 129 | * Set the namespace on the object. 130 | * 131 | * @param string $namespace 132 | */ 133 | protected function setNamespace($namespace) 134 | { 135 | $this->namespace = $namespace; 136 | } 137 | 138 | /** 139 | * Set pathKeys on object. 140 | * 141 | * @param array $pathKeys 142 | */ 143 | protected function setPathKeys(array $pathKeys) 144 | { 145 | $this->pathKeys = $pathKeys; 146 | } 147 | 148 | /** 149 | * Set values collection on object. 150 | * 151 | * @param Collection $values 152 | * 153 | * @return Collection 154 | */ 155 | protected function setValues(Collection $values) 156 | { 157 | return $this->values = $values->map(function ($value) { 158 | if (!$value instanceof BundleItem) { 159 | return ItemFactory::build($value); 160 | } 161 | 162 | return $value; 163 | }); 164 | } 165 | 166 | /** 167 | * Get bundle values from bundle map. 168 | * 169 | * @return Collection 170 | */ 171 | protected function getValuesFromMap() 172 | { 173 | if ($this->bundleMap->bundleMapIsEmpty()) { 174 | $this->bundleMap->mapBundles(); 175 | } 176 | 177 | $this->buildKeys(); 178 | 179 | if (!$this->hasValidNamespace()) { 180 | $this->setValues(collect([])); 181 | } else { 182 | $values = $this->bundleMap->getBundleValues($this->getPathKeys()); 183 | 184 | $this->setValues($values); 185 | } 186 | } 187 | 188 | /** 189 | * Build pathKeys array and set namespace. 190 | */ 191 | protected function buildKeys() 192 | { 193 | $pathKeys = $this->getKeysFromId(); 194 | 195 | $this->setNamespace(array_shift($pathKeys)); 196 | 197 | $this->setPathKeys($pathKeys); 198 | } 199 | 200 | /** 201 | * Get keys from id. 202 | * 203 | * @return array 204 | */ 205 | protected function getKeysFromId() 206 | { 207 | $aliases = config('lang-bundler.aliases'); 208 | 209 | $autoAliases = collect($this->bundleMap->getAutoAliases()) 210 | ->filter(function ($value) { 211 | return $value === $this->id; 212 | }); 213 | 214 | if (in_array($this->id, array_keys($aliases))) { 215 | $this->path = $aliases[$this->id]; 216 | } elseif ($autoAliases->count() === 1) { 217 | $this->path = 'bundles.'.$autoAliases->keys()[0]; 218 | } 219 | 220 | return explode('.', $this->path); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/BundleItems/BundleItem.php: -------------------------------------------------------------------------------- 1 | setNamespace($id); 57 | 58 | $this->setKey($namespace); 59 | 60 | $this->setId($id); 61 | } 62 | 63 | /** 64 | * Return the value of choice. If not null, trans_choice will be called. 65 | * 66 | * @return int|Countable|null 67 | */ 68 | public function hasChoice() 69 | { 70 | return $this->choice; 71 | } 72 | 73 | /** 74 | * Return the translation key for return array. 75 | * 76 | * @return string 77 | */ 78 | public function getKey() 79 | { 80 | return $this->key; 81 | } 82 | 83 | /** 84 | * Get the Laravel lang id of translation. 85 | * 86 | * @return string 87 | */ 88 | public function getId() 89 | { 90 | return $this->id; 91 | } 92 | 93 | /** 94 | * Return the namespace for the translation. 95 | * 96 | * @return string 97 | */ 98 | public function getNamespace() 99 | { 100 | return $this->namespace; 101 | } 102 | 103 | /** 104 | * Return array of valid parameters. 105 | * 106 | * @return array 107 | */ 108 | public function getParameters() 109 | { 110 | return $this->parameters->all(); 111 | } 112 | 113 | /** 114 | * Return the final key/value pair array. 115 | * 116 | * @return array 117 | */ 118 | public function getReturnArray() 119 | { 120 | return [$this->getKey() => $this->getValue()]; 121 | } 122 | 123 | /** 124 | * Return the value. 125 | * 126 | * @return mixed 127 | */ 128 | public function getValue() 129 | { 130 | return $this->value; 131 | } 132 | 133 | /** 134 | * Set choice value on object. 135 | * 136 | * @param int|Countable $value 137 | */ 138 | public function setChoice($value) 139 | { 140 | $this->choice = $value; 141 | } 142 | 143 | /** 144 | * Set only valid parameters. If namespaced, only parameters with 145 | * translation namespace will be set. 146 | * 147 | * @param array $parameters 148 | */ 149 | public function setParameters($parameters) 150 | { 151 | $this->parameters = collect($parameters)->mapWithKeys(function ($value, $key) { 152 | if (!$key = $this->getNamespacedKey($key)) { 153 | return []; 154 | } 155 | 156 | if ($key === 'choice') { 157 | $this->setChoice($value); 158 | } 159 | 160 | return [$key => $value]; 161 | }); 162 | } 163 | 164 | /** 165 | * Get only global and keys with this item's namespace. 166 | * 167 | * @param string $key 168 | * 169 | * @return string|null 170 | */ 171 | protected function getNamespacedKey($key) 172 | { 173 | $keyArray = explode('.', $key); 174 | 175 | if (count($keyArray) !== 2) { 176 | return $key; 177 | } elseif ($keyArray[0] === $this->getNamespace()) { 178 | return $keyArray[1]; 179 | } 180 | } 181 | 182 | /** 183 | * Set the lang value on the object. 184 | * 185 | * @param mixed $value 186 | */ 187 | public function setValue($value) 188 | { 189 | $this->value = $value; 190 | 191 | return $this; 192 | } 193 | 194 | /** 195 | * Set the translation namespace on the object. 196 | */ 197 | protected function setNamespace($id) 198 | { 199 | return $this->namespace = collect(explode('.', $id))->last(); 200 | } 201 | 202 | /** 203 | * Get key for translation value. 204 | * 205 | * @return string 206 | */ 207 | protected function setKey($namespace) 208 | { 209 | $transformMethod = config('lang-bundler.key_transform'); 210 | 211 | if ($transformMethod === 'none') { 212 | return $this->key = $namespace; 213 | } 214 | 215 | return $this->key = $transformMethod($namespace); 216 | } 217 | 218 | /** 219 | * Prefix the translation id with global_key_namespace, if set. 220 | * 221 | * @param string $id 222 | * 223 | * @return string 224 | */ 225 | protected function setId($id) 226 | { 227 | $prefix = config('lang-bundler.global_key_namespace'); 228 | 229 | if (!empty($prefix)) { 230 | $id = $prefix.'.'.$id; 231 | } 232 | 233 | $this->id = $id; 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /tests/Integration/TranslatorTest.php: -------------------------------------------------------------------------------- 1 | copyStubs('bundle2'); 19 | 20 | $this->copyTranslations(); 21 | 22 | $bundle = new Bundle('bundles.bundle2.component1', $this->bundleMap); 23 | 24 | $translations = $this->translator->translateBundle($bundle); 25 | 26 | $expected = $this->getExpected(['homeEn', 'navEn']); 27 | 28 | $this->assertEquals($expected, $translations->all()); 29 | } 30 | 31 | /** 32 | * @test 33 | */ 34 | public function it_translates_bundle_values_with_set_locale() 35 | { 36 | $this->copyStubs('bundle2'); 37 | 38 | $this->copyTranslations(); 39 | 40 | app()->setLocale('ja'); 41 | 42 | $bundle = new Bundle('bundles.bundle2.component1', $this->bundleMap); 43 | 44 | $translations = $this->translator->translateBundle($bundle); 45 | 46 | $expected = $this->getExpected(['homeJa', 'navJa']); 47 | 48 | $this->assertEquals($expected, $translations->all()); 49 | } 50 | 51 | /** 52 | * @test 53 | */ 54 | public function it_accepts_parameters() 55 | { 56 | $this->copyStubs('bundle5'); 57 | 58 | $this->copyTranslations(); 59 | 60 | $bundle = new Bundle('bundles.bundle5', $this->bundleMap); 61 | 62 | $translations = $this->translator->translateBundle( 63 | $bundle, 64 | ['user' => 'Bob', 'sender' => 'Sally'] 65 | ); 66 | 67 | $expected = [ 68 | 'welcome_user' => 'Welcome Bob', 69 | 'message_from' => 'You have a message from Sally', 70 | ]; 71 | 72 | $this->assertEquals($expected, $translations->all()); 73 | } 74 | 75 | /** 76 | * @test 77 | */ 78 | public function parameters_can_be_namespaced() 79 | { 80 | $this->copyStubs('bundle8'); 81 | 82 | $this->copyTranslations(); 83 | 84 | $bundle = new Bundle('bundles.bundle8', $this->bundleMap); 85 | 86 | $translations = $this->translator->translateBundle($bundle, [ 87 | 'welcome_user.user' => 'Bob', 88 | 'message_to.user' => 'Sally', 89 | 'invite_from.user' => 'George', 90 | ]); 91 | 92 | $expected = [ 93 | 'welcome_user' => 'Welcome Bob', 94 | 'message_to' => 'You sent a message to Sally', 95 | 'invite_from' => 'You have an invite from George', 96 | ]; 97 | 98 | $this->assertArraySubset($expected, $translations->all()); 99 | } 100 | 101 | /** 102 | * @test 103 | */ 104 | public function it_transforms_keys_to_study_case() 105 | { 106 | $expected = [ 107 | 'WelcomeUser' => 'Welcome Bob', 108 | 'MessageFrom' => 'You have a message from Sally', 109 | ]; 110 | 111 | $this->transformTest('studly_case', $expected); 112 | } 113 | 114 | /** 115 | * @test 116 | */ 117 | public function it_transforms_keys_to_camel_case() 118 | { 119 | $expected = [ 120 | 'welcomeUser' => 'Welcome Bob', 121 | 'messageFrom' => 'You have a message from Sally', 122 | ]; 123 | 124 | $this->transformTest('camel_case', $expected); 125 | } 126 | 127 | /** 128 | * @test 129 | */ 130 | public function it_transforms_keys_to_snake_case() 131 | { 132 | $expected = [ 133 | 'welcome_user' => 'Welcome Bob', 134 | 'message_from' => 'You have a message from Sally', 135 | ]; 136 | 137 | $this->transformTest('snake_case', $expected, 'bundle6'); 138 | } 139 | 140 | /** 141 | * @test 142 | */ 143 | public function it_adds_a_key_prefix() 144 | { 145 | $this->copyStubs('bundle7'); 146 | 147 | $this->copyTranslations(); 148 | 149 | app()['config']->set('lang-bundler.global_key_namespace', 'translations'); 150 | 151 | $bundle = new Bundle('bundles.bundle7', $this->bundleMap); 152 | 153 | $translations = $this->translator->translateBundle($bundle); 154 | 155 | $expected = $this->getExpected('bundle7'); 156 | 157 | $this->assertEquals($expected, $translations->all()); 158 | } 159 | 160 | /** 161 | * Perform key transformation test. 162 | * 163 | * @param string $case 164 | * @param array $expected 165 | * @param string $bundle 166 | */ 167 | protected function transformTest($case, $expected, $bundleName = 'bundle5') 168 | { 169 | $this->copyStubs($bundleName); 170 | 171 | $this->copyTranslations(); 172 | 173 | app()['config']->set('lang-bundler.key_transform', $case); 174 | 175 | $bundle = new Bundle('bundles.'.$bundleName, $this->bundleMap); 176 | 177 | $translations = $this->translator->translateBundle( 178 | $bundle, 179 | ['user' => 'Bob', 'sender' => 'Sally'] 180 | ); 181 | 182 | $this->assertEquals($expected, $translations->all()); 183 | } 184 | 185 | /** 186 | * @test 187 | */ 188 | public function if_choice_is_not_null_trans_choice_is_run() 189 | { 190 | $this->copyStubs('bundle8'); 191 | 192 | $this->copyTranslations(); 193 | 194 | $bundle = new Bundle('bundles.bundle8', $this->bundleMap); 195 | 196 | $translations = $this->translator->translateBundle( 197 | $bundle, 198 | ['user' => 'Bob', 'inbox_status.choice' => 3] 199 | ); 200 | 201 | $this->assertEquals('You have new messages', $translations['inbox_status']); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /tests/Unit/BundleMapTest.php: -------------------------------------------------------------------------------- 1 | bundleMap->mapBundles(); 19 | 20 | $this->assertEquals([], $map); 21 | } 22 | 23 | /** 24 | * @test 25 | */ 26 | public function it_maps_a_single_dimension_array() 27 | { 28 | $this->copyStubs('bundle1'); 29 | 30 | $map = $this->bundleMap->mapBundles(); 31 | 32 | $expected = $this->getExpected('bundle1'); 33 | 34 | $this->assertEquals($expected, $map); 35 | } 36 | 37 | /** 38 | * @test 39 | */ 40 | public function it_maps_a_multidimensional_array() 41 | { 42 | $this->copyStubs('bundle2'); 43 | 44 | $map = $this->bundleMap->mapBundles(); 45 | 46 | $expected = $this->getExpected('bundle2'); 47 | 48 | $this->assertEquals($expected, $map); 49 | } 50 | 51 | /** 52 | * @test 53 | */ 54 | public function it_maps_multiple_files() 55 | { 56 | $bundles = ['bundle1', 'bundle2']; 57 | 58 | $this->copyStubs($bundles); 59 | 60 | $map = $this->bundleMap->mapBundles(); 61 | 62 | $expected = $this->getExpected($bundles); 63 | 64 | $this->assertEquals($expected, $map); 65 | } 66 | 67 | /** 68 | * @test 69 | */ 70 | public function it_maps_layered_directories() 71 | { 72 | $this->copyStubs('components'); 73 | 74 | $map = $this->bundleMap->mapBundles(); 75 | 76 | $expected = [ 77 | 'components' => [ 78 | 'sub-components' => $this->getExpected('bundle4'), 79 | 'bundle3' => $this->getExpected('bundle3')['bundle3'], 80 | ], 81 | ]; 82 | 83 | $this->assertEquals($expected, $map); 84 | } 85 | 86 | /** 87 | * @test 88 | */ 89 | public function it_gets_values_for_a_single_dimension_array() 90 | { 91 | $this->copyStubs('bundle1'); 92 | 93 | $bundle = new Bundle('bundles.bundle1', $this->bundleMap); 94 | 95 | $values = $this->bundleMap->getBundleValues($bundle->getPathKeys())->all(); 96 | 97 | $expected = $this->getExpected('bundle1', true); 98 | 99 | $this->assertEquals($expected, $values); 100 | } 101 | 102 | /** 103 | * @test 104 | */ 105 | public function it_gets_values_for_a_multidimensional_array() 106 | { 107 | $this->copyStubs('bundle2'); 108 | 109 | $bundle = new Bundle('bundles.bundle2.component2', $this->bundleMap); 110 | 111 | $values = $this->bundleMap->getBundleValues($bundle->getPathKeys())->all(); 112 | 113 | $expected = $this->getExpected('bundle2', true)['component2']; 114 | 115 | $this->assertEquals($expected, $values); 116 | } 117 | 118 | /** 119 | * @test 120 | */ 121 | public function it_gets_values_when_multiple_files_present() 122 | { 123 | $this->copyStubs(['bundle1', 'bundle2']); 124 | 125 | $bundle = new Bundle('bundles.bundle2.component1', $this->bundleMap); 126 | 127 | $values = $this->bundleMap->getBundleValues($bundle->getPathKeys())->all(); 128 | 129 | $expected = $this->getExpected('bundle2', true)['component1']; 130 | 131 | $this->assertEquals($expected, $values); 132 | } 133 | 134 | /** 135 | * @test 136 | */ 137 | public function it_gets_values_from_within_directories() 138 | { 139 | $this->copyStubs('components'); 140 | 141 | $bundle = new Bundle('bundles.components.bundle3.forum.component3', $this->bundleMap); 142 | 143 | $values = $this->bundleMap->getBundleValues($bundle->getPathKeys())->all(); 144 | 145 | $expected = $this->getExpected('bundle3', true)['forum']['component3']; 146 | 147 | $this->assertEquals($expected, $values); 148 | } 149 | 150 | /** 151 | * @test 152 | */ 153 | public function it_gets_values_from_within_layed_directories() 154 | { 155 | $this->copyStubs('components'); 156 | 157 | $bundle = new Bundle('bundles.components.sub-components.bundle4', $this->bundleMap); 158 | 159 | $values = $this->bundleMap->getBundleValues($bundle->getPathKeys())->all(); 160 | 161 | $expected = $this->getExpected('bundle4', true); 162 | 163 | $this->assertEquals($expected, $values); 164 | } 165 | 166 | /** 167 | * @test 168 | */ 169 | public function invalid_bundle_name_returns_empty_array() 170 | { 171 | $this->copyStubs('components'); 172 | 173 | $bundle = new Bundle('bundles.components.none', $this->bundleMap); 174 | 175 | $values = $this->bundleMap->getBundleValues($bundle->getPathKeys())->all(); 176 | 177 | $this->assertEquals([], $values); 178 | } 179 | 180 | /** 181 | * @test 182 | */ 183 | public function it_finds_bundles_with_registered_alias() 184 | { 185 | $this->copyStubs('bundle2'); 186 | 187 | app()['config']->set('lang-bundler.aliases', [ 188 | 'test' => 'bundles.bundle2.component1', 189 | ]); 190 | 191 | $bundle = new Bundle('test', $this->bundleMap); 192 | 193 | $values = $this->bundleMap->getBundleValues($bundle->getPathKeys())->all(); 194 | 195 | $expected = $this->getExpected('bundle2', true)['component1']; 196 | 197 | $this->assertEquals($expected, $values); 198 | } 199 | 200 | /** 201 | * @test 202 | */ 203 | public function it_finds_bundles_from_auto_registered_alias() 204 | { 205 | $this->copyStubs('components'); 206 | 207 | $bundle = new Bundle('bundle4', $this->bundleMap); 208 | 209 | $values = $this->bundleMap->getBundleValues($bundle->getPathKeys())->all(); 210 | 211 | $expected = $this->getExpected('bundle4', true); 212 | 213 | $this->assertEquals($expected, $values); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /tests/stubs/ExpectedResults.php: -------------------------------------------------------------------------------- 1 | {$bundles}()[$bundles]; 18 | } 19 | 20 | if (!is_array($bundles)) { 21 | $bundles = [$bundles]; 22 | } 23 | 24 | return $this->mergeBundles($bundles); 25 | } 26 | 27 | /** 28 | * Merge bundles into single array. 29 | * 30 | * @param array $bundles 31 | * 32 | * @return array 33 | */ 34 | private function mergeBundles(array $bundles) 35 | { 36 | $expected = []; 37 | 38 | foreach ($bundles as $bundle) { 39 | $expected = array_merge($expected, $this->{$bundle}()); 40 | } 41 | 42 | return $expected; 43 | } 44 | 45 | /** 46 | * Expected results for bundle1 stub. 47 | * 48 | * @return array 49 | */ 50 | private function bundle1() 51 | { 52 | return [ 53 | 'bundle1' => [ 54 | 'home.welcome', 55 | 'home.signup', 56 | 'home.login', 57 | 'nav.home', 58 | 'nav.top', 59 | ], 60 | ]; 61 | } 62 | 63 | /** 64 | * Expected results for bundle2 stub. 65 | * 66 | * @return array 67 | */ 68 | private function bundle2() 69 | { 70 | return [ 71 | 'bundle2' => [ 72 | 'component1' => [ 73 | 'home.welcome', 74 | 'home.signup', 75 | 'home.login', 76 | 'nav.home', 77 | 'nav.top', 78 | ], 79 | 80 | 'component2' => [ 81 | 'home.welcome', 82 | 'home.signup', 83 | 'home.login', 84 | 'nav.home', 85 | 'nav.top', 86 | ], 87 | ], 88 | ]; 89 | } 90 | 91 | /** 92 | * Expected results for bundle3 stub. 93 | * 94 | * @return array 95 | */ 96 | private function bundle3() 97 | { 98 | return [ 99 | 'bundle3' => [ 100 | 'component1' => [ 101 | 'home.welcome', 102 | 'home.signup', 103 | 'home.login', 104 | 'nav.home', 105 | 'nav.top', 106 | ], 107 | 108 | 'forum' => [ 109 | 'component2' => [ 110 | 'home.welcome', 111 | 'home.signup', 112 | 'home.login', 113 | 'nav.home', 114 | 'nav.top', 115 | ], 116 | 117 | 'component3' => [ 118 | 'payment.creditcard', 119 | 'payment.date', 120 | 'payment.submit', 121 | ], 122 | ], 123 | ], 124 | ]; 125 | } 126 | 127 | /** 128 | * Expected results for bundle4 stub. 129 | * 130 | * @return array 131 | */ 132 | private function bundle4() 133 | { 134 | return [ 135 | 'bundle4' => [ 136 | 'home.welcome', 137 | 'home.signup', 138 | 'home.login', 139 | 'nav.home', 140 | 'nav.top', 141 | ], 142 | ]; 143 | } 144 | 145 | /** 146 | * Expected results for English home translations. 147 | * 148 | * @return array 149 | */ 150 | private function homeEn() 151 | { 152 | return [ 153 | 'welcome' => 'Welcome', 154 | 'signup' => 'Signup', 155 | 'login' => 'Login', 156 | ]; 157 | } 158 | 159 | /** 160 | * Expected results for English nav translations. 161 | * 162 | * @return array 163 | */ 164 | private function navEn() 165 | { 166 | return [ 167 | 'home' => 'Home', 168 | 'top' => 'Top', 169 | ]; 170 | } 171 | 172 | /** 173 | * Expected results for English home translations. 174 | * 175 | * @return array 176 | */ 177 | private function homeJa() 178 | { 179 | return [ 180 | 'welcome' => 'ようこそ', 181 | 'signup' => 'サインアップ', 182 | 'login' => 'ログイン', 183 | ]; 184 | } 185 | 186 | /** 187 | * Expected results for English nav translations. 188 | * 189 | * @return array 190 | */ 191 | private function navJa() 192 | { 193 | return [ 194 | 'home' => 'ホーム', 195 | 'top' => 'トップ', 196 | ]; 197 | } 198 | 199 | /** 200 | * Expected results for bundle7 translations. 201 | * 202 | * @return array 203 | */ 204 | private function bundle7() 205 | { 206 | return [ 207 | 'welcome' => 'Welcome', 208 | 'signup' => 'Signup', 209 | 'login' => 'Login', 210 | 'home' => 'Home', 211 | 'top' => 'Top', 212 | ]; 213 | } 214 | 215 | /** 216 | * Expected results for bundle10 translations. 217 | * 218 | * @return array 219 | */ 220 | private function bundle10() 221 | { 222 | return [ 223 | 'Welcome_user' => 'Welcome :user', 224 | 'message_to' => 'YOU SENT A MESSAGE TO :USER', 225 | 'InviteFrom' => 'You have an invite from :user', 226 | 'newKey' => 'You have an invite from :user', 227 | ]; 228 | } 229 | 230 | /** 231 | * Expected results for months translations. 232 | * 233 | * @return array 234 | */ 235 | private function months() 236 | { 237 | return array_values([ 238 | 'january' => 'Jan', 239 | 'february' => 'Feb', 240 | 'march' => 'March', 241 | 'april' => 'April', 242 | 'may' => 'May', 243 | 'june' => 'June', 244 | 'july' => 'July', 245 | 'august' => 'Aug', 246 | 'september' => 'Sept', 247 | 'october' => 'Oct', 248 | 'november' => 'Nov', 249 | 'december' => 'Dec', 250 | ]); 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Lang Bundler 2 | [![Latest Stable Version](https://img.shields.io/packagist/v/zachleigh/laravel-lang-bundler.svg)](//packagist.org/packages/zachleigh/laravel-lang-bundler) 3 | [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](//packagist.org/packages/zachleigh/laravel-lang-bundler) 4 | [![Build Status](https://img.shields.io/travis/zachleigh/laravel-lang-bundler/master.svg)](https://travis-ci.org/zachleigh/laravel-lang-bundler) 5 | [![Quality Score](https://img.shields.io/scrutinizer/g/zachleigh/laravel-lang-bundler.svg)](https://scrutinizer-ci.com/g/zachleigh/laravel-lang-bundler/) 6 | [![StyleCI](https://styleci.io/repos/72352058/shield?style=flat)](https://styleci.io/repos/72352058) 7 | [![Total Downloads](https://img.shields.io/packagist/dt/zachleigh/laravel-lang-bundler.svg)](https://packagist.org/packages/zachleigh/laravel-lang-bundler) 8 | 9 | ##### Make bundles of translation values. 10 | 11 | ### Contents 12 | - [Why](#why) 13 | - [Upgrade Information](#upgrade-information) 14 | - [Install](#install) 15 | - [Usage](#usage) 16 | - [Advanced Usage](#advanced-usage) 17 | - [Commands](#commands) 18 | - [Configuration](#configuration) 19 | - [Limitations](#limitations) 20 | - [Testing](#testing) 21 | - [Contributing](#contributing) 22 | 23 | ### Why 24 | Why use this package? Because this sucks: 25 | ```html 26 | 35 | ``` 36 | And this is so much better: 37 | ```html 38 | toJson() }} 40 | > 41 | ``` 42 | 43 | ### Upgrade Information 44 | ##### Version 1.0.* to Version 1.1.* 45 | Version 1.1.0 drops support for PHP versions lower than 7.1. 46 | 47 | ##### Version 0.9.* to Version 1.0.0 48 | Version 1.0.0 is more a confimation of the current api and usage than anything else. Adds support for Laravel 5.4. If using Laravel 5.3, please use [Version 0.9.11](https://github.com/zachleigh/laravel-lang-bundler/tree/v0.9.11): 49 | ``` 50 | composer require zachleigh/laravel-lang-bundler:0.9.* 51 | ``` 52 | 53 | ### Install 54 | ##### 1. Install through composer 55 | ``` 56 | composer require zachleigh/laravel-lang-bundler 57 | ``` 58 | 59 | ##### 2. Register the service provider 60 | In Laravel's config/app.php file, add the service provider to the array with the 'providers' key. 61 | ``` 62 | LaravelLangBundler\ServiceProvider::class 63 | ``` 64 | 65 | ##### 3. Publish the config file: 66 | ``` 67 | php artisan vendor:publish --tag=config 68 | ``` 69 | 70 | ##### 4. Create a 'bundles' directory in resources/lang/. 71 | Do it manually or use the command: 72 | ``` 73 | php artisan langb:start 74 | ``` 75 | 76 | ### Usage 77 | ##### 1. Make a bundle. 78 | Imagine we have two lang files, one called 'home.php' and one called 'user.php'. 79 | 80 | home.php 81 | ```php 82 | 'welcome' => 'Welcome!', 83 | 'login' => 'Login', 84 | 'signup' => 'Signup', 85 | ``` 86 | 87 | user.php 88 | ```php 89 | 'profile' => 'Your Profile', 90 | 'friends' => 'Your Friends', 91 | 'body' => 'Enter body below', 92 | ``` 93 | We want all of these values in one bundle. 94 | 95 | Simply register your bundle as an array anywhere in the bundles directory. For example, in the bundles directory, you could create a folder called 'components' and in it a file called 'bundle_name' that looks like this: 96 | ```php 97 | return [ 98 | 'home.welcome', 99 | 'home.login', 100 | 'home.signup', 101 | 'user.profile', 102 | 'user.friends', 103 | 'user.body' 104 | ]; 105 | ``` 106 | Obviously, 'bundle_name' is the name of the bundle. The other values represent keys found in the above lang files. 107 | 108 | Like in other lang folders, any file/folder in the bundles directory is treated as a level in an array. So in the above example, our file path looks like this: 109 | 110 | lang/bundles/components/bundle_name.php 111 | 112 | The path for the 'bundle_name' bundle would be 'bundles.components.bundle_name'. It is also possible to create multiple named bundles within a single file, but this is not recommended because you can not use auto-aliasing for multi-bundle files. 113 | 114 | ##### 2. Get the bundle using the transB() helper function. 115 | Get your translated bundle by passing the bundle path to the transB() helper function. 116 | ```php 117 | transB('bundles.components.bundle_name'); 118 | ``` 119 | Or use the auto-aliased name: 120 | ```php 121 | transB('bundle_name'); 122 | ``` 123 | transB() returns a collection of translated values keyed by the original translation key. Continuing the example above, transB() would give us a collection that contains the following array: 124 | ```php 125 | [ 126 | 'welcome' => 'Welcome!', 127 | 'login' => 'Login', 128 | 'signup' => 'Signup', 129 | 'profile' => 'Your Profile', 130 | 'friends' => 'Your Friends', 131 | 'body' => 'Enter body below', 132 | ]; 133 | ``` 134 | 135 | ##### 3. Pass parameters to your bundle. 136 | Like with the standard trans() function, you may pass parameters to the transB() function as the second argument. 137 | ```php 138 | transB('bundle_name', ['parameterName' => $value]); 139 | ``` 140 | If your bundle has conflicting parameter names, you can namespace them. In this example, three values require a `user` parameter. 141 | 142 | user.php translation file: 143 | ```php 144 | return [ 145 | 'welcome_user' => 'Welcome :user', 146 | 'message_to' => 'You sent a message to :user', 147 | 'invite_from' => 'You have an invite from :user' 148 | ]; 149 | ``` 150 | Bundle file: 151 | ```php 152 | return [ 153 | 'user.welcome_user', 154 | 'user.message_to', 155 | 'user.invite_from' 156 | ]; 157 | ``` 158 | Avoid the naming conflict by namespacing the parameters when passing them to transB(): 159 | ```php 160 | transB('bundle_name', [ 161 | 'welcome_user.user' => 'Bob', 162 | 'message_to.user' => 'Sally', 163 | 'invite_from.user' => 'George' 164 | ]); 165 | ``` 166 | 167 | ##### 4. Pluralize values 168 | It is possible to pluralize lang items by passing a namespaced 'choice' parameter in the transB() function parameters. For example, if our lang file value looked like this: 169 | ```php 170 | 'inbox_status' => 'You have a new message.|You have new messages' 171 | ``` 172 | We could register it in our bundle normally: 173 | ```php 174 | 'home.inbox_status' 175 | ``` 176 | And then when calling transB(), pass a parameter called 'inbox_status.choice' with the desired choice value: 177 | ```php 178 | transB('bundle_name', ['inbox_status.choice' => 3]); 179 | ``` 180 | The result will look be the pluralized string "You have new messages". 181 | 182 | ### Advanced Usage 183 | #### Modify return keys and values 184 | To modify the key and value in the returned translation array, use the bundle_item() helper on a specific bundle item. 185 | ```php 186 | bundle_item($id, $type, $parameters = []); 187 | ``` 188 | $id is the lang key. $type must be in the following format: 'target_type'. 'target' declares what item is to be affected by the modification and can be set to either 'value', 'key', or 'both'. 'type' declares the type of modification (callback, change etc.). $parameters is an array of parameters to be sent to the class that performs the modification. 189 | 190 | If using the same example as above we wanted to convert the 'welcome_user' value to all caps, we could accomplish it by using the bundle_item() helper function in the bundle file. 191 | user.php translation file: 192 | ```php 193 | return [ 194 | 'welcome_user' => 'Welcome :user', 195 | 'message_to' => 'You sent a message to :user', 196 | 'invite_from' => 'You have an invite from :user' 197 | ]; 198 | ``` 199 | Bundle file: 200 | ```php 201 | return [ 202 | bundle_item('user.welcome_user', 'value_strtoupper'), 203 | 'user.message_to', 204 | 'user.invite_from' 205 | ]; 206 | ``` 207 | Wrap the bundle key 'user.welcome_user' in the bundle_item() global function and pass the translation key ($id) plus the type (perform a 'strtoupper' on the returned 'value'). This returns the following values (assuming a non-namespaced user variable with the value 'Bob'): 208 | ``` 209 | [ 210 | 'welcome_user' => 'WELCOME BOB', 211 | 'message_to' => 'You sent a message to Bob', 212 | 'invite_from' => 'You have an invite from Bob' 213 | ]; 214 | ``` 215 | 216 | If we wanted to do the same to the key, we could do this: 217 | ```php 218 | return [ 219 | bundle_item('user.welcome_user', 'key_strtoupper'), 220 | 'user.message_to', 221 | 'user.invite_from' 222 | ]; 223 | ``` 224 | 225 | Or, if we wanted to perform the modification on both the key and the value: 226 | ```php 227 | return [ 228 | bundle_item('user.welcome_user', 'both_strtoupper'), 229 | 'user.message_to', 230 | 'user.invite_from' 231 | ]; 232 | ``` 233 | 234 | ##### Available modifiers 235 | ###### callback 236 | Perform a callback on a key or value. Requires a 'callback' parameter. 237 | ```php 238 | bundle_item('user.welcome_user', 'value_callback', [ 239 | 'callback' => 'function_name' 240 | ]), 241 | ``` 242 | 243 | ###### change 244 | Change a key to a new value. Does nothing to values. Requires 'new' parameter. 245 | ```php 246 | bundle_item('user.invite_from', 'key_change', [ 247 | 'new' => 'newKey' 248 | ]), 249 | ``` 250 | ###### explode 251 | Explode by given delimiter. Does nothing to key. Requires 'delimiter' parameter. 252 | ```php 253 | bundle_item('user.invite_from', 'value_explode', [ 254 | 'delimiter' => ' ' 255 | ]), 256 | ``` 257 | 258 | ###### strtolower 259 | Lowercase entrire string. 260 | ```php 261 | bundle_item('home.invite_from', 'value_strtolower') 262 | ``` 263 | 264 | ###### strtoupper 265 | Capitalize entire string. 266 | ```php 267 | bundle_item('home.invite_from', 'value_strtoupper') 268 | ``` 269 | 270 | ###### ucfirst 271 | Make first character in string capitalized. 272 | ```php 273 | bundle_item('home.invite_from', 'value_ucfirst') 274 | ``` 275 | 276 | ###### values 277 | If translation value is an array, run array_values() on array and return only values keyed by integers. Does nothing to keys. 278 | ```php 279 | bundle_item('home.months', 'value_values') 280 | ``` 281 | 282 | ##### Creating your own modifier 283 | Use the 'mod' command to create a new mod class in App/LangBundler/Mods: 284 | ``` 285 | langb:mod {name} 286 | ``` 287 | 288 | There are two abstract methods that must be implemented in your class: 289 | ```php 290 | /** 291 | * Alter key and return. 292 | * 293 | * @param string $key 294 | * 295 | * @return string 296 | */ 297 | abstract public function key($key); 298 | 299 | /** 300 | * Alter value and return. 301 | * 302 | * @param mixed $value 303 | * 304 | * @return mixed 305 | */ 306 | abstract public function value($value); 307 | ``` 308 | The same class is used to modify both the value and key. Define your modification and return the desired key/value. 309 | 310 | ### Commands 311 | ##### php artisan langb:start 312 | Get started by creating a bundles directory in your lang folder. 313 | 314 | ##### php artisan langb:new {path} 315 | Create a new bundle file located at path. For example: 316 | ``` 317 | php artisan langb:new components.user.profile 318 | ``` 319 | This would create the file lang/bundles/components/user/profile.php with an empty returned array. 320 | 321 | ##### php artisan langb:mod {name} 322 | Create an empty mod template in App/LangBundler/Mods. 323 | 324 | ### Configuration 325 | ##### aliases 326 | To shorten the name of bundles, you can register aliases in config. 327 | ```php 328 | 'aliases' [ 329 | 'alias' => 'full.path.to.bundle' 330 | ]; 331 | ``` 332 | And then simply use the alias istead of the path in transB(): 333 | ```php 334 | transB('alias'); 335 | ``` 336 | 337 | ##### key_transform 338 | If you wish to transform lang file keys to snake_case, StudlyCase, or camelCase, set `key_transform` to 'snake_case', 'studly_case', or 'camel_case'. Default value is 'none'. 339 | For example, this bundle contains snake cased variables: 340 | ```php 341 | return [ 342 | 'user.welcome_user', 343 | 'user.message_to', 344 | 'user.invite_from' 345 | ]; 346 | ``` 347 | But in your javascript, you want to use came cased variables, set `key_transform` to 'camel_case' to get this bundle: 348 | ```php 349 | return [ 350 | 'welcomeUser' => 'Welcome user!', 351 | 'messageTo' => 'Message to user', 352 | 'inviteFrom' => 'You have an invitation from user!', 353 | ]; 354 | ``` 355 | Many other simple string functions (ucfirst, strtoupper, etc.) also work. 356 | 357 | key_transform is global and will transform all keys in your project. If you wish to transform a single key, see [modify return keys and values](#modify-return-keys-and-values). 358 | 359 | ##### global_key_namespace 360 | If you keep all your translations in a single file, you can set `global_key_namespace` to the name of the file to save yourself some typing. For example, if all your translations are in a file called 'translations.php', you would have to register a bundle like this: 361 | ```php 362 | return [ 363 | 'bundle_name' => [ 364 | 'translations.home', 365 | 'translations.navigation', 366 | 'translations.menu', 367 | 'translations.login' 368 | ]; 369 | ]; 370 | ``` 371 | However, if you set `global_key_namespace` to 'translations', you could register it like this: 372 | ```php 373 | return [ 374 | 'bundle_name' => [ 375 | 'home', 376 | 'navigation', 377 | 'menu', 378 | 'login' 379 | ]; 380 | ]; 381 | ``` 382 | 383 | ### Testing 384 | ``` 385 | composer test 386 | ``` 387 | 388 | ### Contributing 389 | Contributions are more than welcome. Fork, improve and make a pull request. For bugs, ideas for improvement or other, please create an [issue](https://github.com/zachleigh/laravel-lang-bundler/issues). 390 | --------------------------------------------------------------------------------