├── README.md
├── composer.json
└── src
├── Xinax
└── LaravelGettext
│ ├── Adapters
│ ├── AdapterInterface.php
│ └── LaravelAdapter.php
│ ├── Commands
│ ├── BaseCommand.php
│ ├── GettextCreate.php
│ └── GettextUpdate.php
│ ├── Composers
│ └── LanguageSelector.php
│ ├── Config
│ ├── ConfigManager.php
│ └── Models
│ │ └── Config.php
│ ├── Exceptions
│ ├── DirectoryNotFoundException.php
│ ├── FileCreationException.php
│ ├── LocaleFileNotFoundException.php
│ ├── LocaleNotSupportedException.php
│ ├── MissingPhpGettextModuleException.php
│ ├── RequiredConfigurationFileException.php
│ ├── RequiredConfigurationKeyException.php
│ └── UndefinedDomainException.php
│ ├── Facades
│ └── LaravelGettext.php
│ ├── FileLoader
│ ├── Cache
│ │ └── ApcuFileCacheLoader.php
│ └── MoFileLoader.php
│ ├── FileSystem.php
│ ├── LaravelGettext.php
│ ├── LaravelGettextServiceProvider.php
│ ├── Middleware
│ └── GettextMiddleware.php
│ ├── Storages
│ ├── MemoryStorage.php
│ ├── SessionStorage.php
│ └── Storage.php
│ ├── Support
│ └── helpers.php
│ ├── Testing
│ ├── Adapter
│ │ └── TestAdapter.php
│ └── BaseTestCase.php
│ └── Translators
│ ├── BaseTranslator.php
│ ├── Gettext.php
│ ├── Symfony.php
│ └── TranslatorInterface.php
└── config
├── .gitkeep
└── config.php
/README.md:
--------------------------------------------------------------------------------
1 | # Laravel Gettext
2 |
3 | *Laravel Gettext* is a translation package compatible with the [Laravel Framework](https://github.com/zerospam/laravel-gettext). It provides a simple way to add localization support to Laravel applications. It is designed to work with *GNU gettext* and *Poedit*. Former versions of this package (before 4.x) works with the native php-gettext module. Current versions uses the Symfony translation package by default instead of native php extension.
4 |
5 | [](https://travis-ci.org/zerospam/laravel-gettext) [laravel-gettext for Laravel 8](https://github.com/zerospam/laravel-gettext/tree/8.0.4)
6 |
7 | [](https://travis-ci.org/zerospam/laravel-gettext) [laravel-gettext for Laravel 6](https://github.com/zerospam/laravel-gettext/tree/7.2.0)
8 |
9 | Laravel Latest Long Term Support Version is Laravel 6, we do not recommend using Laravel version < 6
10 |
11 | ### 1. Requirements
12 |
13 | - [Laravel](https://laravel.com)
14 | - [Composer](https://getcomposer.org/)
15 |
16 | #### 1.1 Optional
17 |
18 | ##### 1.1.1 APCU
19 |
20 | APCU extension installed - http://php.net/manual/en/book.apcu.php
21 |
22 | If the APCU php extension is installed, the library will use the memory to cache the loaded translation to avoid having to parse the translation file (mo/po) at each request.
23 |
24 | The cache is automatically invalidated when there is a change in the translation file.
25 |
26 |
27 | ##### 1.1.2 gettext
28 |
29 | Optional requirements if you want to use the native php-gettext extension:
30 |
31 | - php-gettext - http://www.php.net/manual/en/book.gettext.php
32 | - GNU gettext on system (and production server!) - https://www.gnu.org/software/gettext/
33 |
34 | > You will need to update the 'handler' option to 'gettext' in order to use the native php-gettext module.
35 |
36 | ### 2. Install
37 |
38 | Once it's installed, Laravel will discover automatically the provider and load it. (Only for 5.5+)
39 |
40 | ```bash
41 | composer require zerospam/laravel-gettext
42 | ```
43 |
44 |
45 | Now you need to publish the configuration file in order to set your own application values:
46 |
47 | ```bash
48 | php artisan vendor:publish
49 | ```
50 |
51 | This command creates the package configuration file in: ```config/laravel-gettext.php```.
52 |
53 | You also need to register the LaravelGettext middleware in the ```app/Http/Kernel.php``` file:
54 |
55 | ```php
56 | protected $middlewareGroups = [
57 | 'web' => [
58 | // ...
59 | \Xinax\LaravelGettext\Middleware\GettextMiddleware::class,
60 | ],
61 | // ...
62 | ]
63 | ```
64 |
65 | > Be sure to add the line after ```Illuminate\Session\Middleware\StartSession```, otherwise the locale won't be saved into the session.
66 |
67 | ### 3. Configuration
68 |
69 | At this time your application has full gettext support. Now you need to set some configuration values in ```config/laravel-gettext.php```.
70 |
71 | ```php
72 | /**
73 | * Default locale: this will be the default for your application all
74 | * localized strings. Is to be supposed that all strings are written
75 | * on this language.
76 | */
77 | 'locale' => 'es_ES',
78 | ```
79 |
80 | ```php
81 | /**
82 | * Supported locales: An array containing all allowed languages
83 | */
84 | 'supported-locales' => array(
85 | 'es_ES',
86 | 'en_US',
87 | 'it_IT',
88 | 'es_AR',
89 | ),
90 | ```
91 |
92 | ```php
93 | /**
94 | * Default charset encoding.
95 | */
96 | 'encoding' => 'UTF-8',
97 | ```
98 |
99 | Ok, now it's configured. It's time to generate the directory structure and translation files for the first time.
100 |
101 | > Make sure you have write permissions on ```resources/``` before you run this command
102 |
103 | ```bash
104 | php artisan gettext:create
105 | ```
106 |
107 | With this command the needed directories and files are created on **resources/lang/i18n**
108 |
109 | ### 4. Workflow
110 |
111 | ##### A. Write strings :D
112 |
113 | By default *LaravelGettext* looks on app/Http/Controllers and resources/views recursively searching for translations. Translations are all texts printed with the **_i()** function. Let's look a simple view example:
114 |
115 | ```php
116 | // an example view file
117 | echo 'Non translated string';
118 | echo _i('Translated string');
119 | echo _i('Another translated string');
120 | // with parameter
121 | $str = 'parameter';
122 | $n = 2;
123 | echo _i('Translated string with %s', $str);
124 | echo _i('%dnd translated string with %s', [$n, $str]);
125 | ```
126 |
127 | ```php
128 | // an example view in blade
129 | {{ _i('Translated string') }}
130 | ```
131 |
132 | > Poedit doesn't "understand" blade syntax. When using blade views you must run ```php artisan gettext:update``` in order to compile all blade views to plain php before update the translations in Poedit
133 |
134 |
135 | ##### B. Plural strings
136 |
137 | The plural translations follow the same pattern above. Plural translations are all texts printed with the **_n()** function, and it follow the php ngettext. Let's look a simple view example:
138 |
139 | ```php
140 | // an example view file
141 | $n = 2;
142 | echo ($n > 1) ? 'Non translated plural string' : 'Non translated string';
143 | echo _n('Translated string', 'Translated plural string', $n);
144 | // with parameter
145 | $str = 'parameter';
146 | echo _n('Translated string %s', 'Translated plural string %s', 2, $str);
147 | ```
148 |
149 | ```php
150 | // an example view in blade
151 | {{ _n('Translated string', 'Translated plural string', $n) }}
152 | ```
153 |
154 | > The Poedit keywords are defined in configuration file with this default pattern:
155 | ```php
156 | ['_n:1,2', 'ngettext:1,2']
157 | ```
158 | See Plural forms used by Poedit to configure for your language.
159 |
160 | ###### With Symfony
161 |
162 | If you're using [Symfony](http://symfony.com/doc/current/translation.html) as your translation backend, you have access to their plurals syntax with the ``_s`` method. In Poedit it will be considered as a single line instead of a plural.
163 | ```php
164 | // an example view file
165 | $n = 2;
166 | echo ($n > 1) ? 'Non translated plural string' : 'Non translated string';
167 | echo _s('Translated string|Translated plural string', $n);
168 | // with parameter
169 | $str = 'parameter';
170 | echo _n('Translated string %s|Translated plural string %s', 2, $str);
171 | ```
172 | With symfony complex syntax:
173 | ```php
174 | echo _s('{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples', $n);
175 | // with parameter
176 | $str = 'red';
177 | echo _s('{0} There are no %s apples|{1} There is one %s apple|]1,Inf[ There are %count% %s apples', 2, $str);
178 | ```
179 |
180 |
181 | ##### C. Translate with Poedit
182 |
183 | Open the PO file for the language that you want to translate with Poedit. The PO files are located by default in **resources/lang/i18n/[locale]/LC_MESSAGES/[domain].po**. If you have multiple gettext domains, one file is generated by each domain.
184 |
185 |
186 |
187 | Once Poedit is loaded press the Update button to load all localized strings. You can repeat this step anytime you add a new localized string.
188 |
189 | Fill translation fields in Poedit and save the file. The first time that you do this the MO files will be generated for each locale.
190 |
191 | ##### C. Runtime methods
192 |
193 | To change configuration on runtime you have these methods:
194 |
195 | ```php
196 | /**
197 | * Sets the Current locale.
198 | * Example param value: 'es_ES'
199 | *
200 | * @param mixed $locale the locale
201 | * @return LaravelGettext
202 | */
203 | LaravelGettext::setLocale($locale);
204 | ```
205 |
206 | ```php
207 | /**
208 | * Gets the Current locale.
209 | * Example returned value: 'es_ES'
210 | *
211 | * @return String
212 | */
213 | LaravelGettext::getLocale();
214 | ```
215 |
216 | ```php
217 | /**
218 | * Gets the language portion of the locale.
219 | * Eg from en_GB, returns en
220 | *
221 | * @return mixed
222 | */
223 | LaravelGettext::getLocaleLanguage()
224 | ```
225 |
226 | ```php
227 | /**
228 | * Sets the Current encoding.
229 | * Example param value: 'UTF-8'
230 | *
231 | * @param mixed $encoding the encoding
232 | * @return LaravelGettext
233 | */
234 | LaravelGettext::setEncoding($encoding);
235 | ```
236 |
237 | ```php
238 | /**
239 | * Gets the Current encoding.
240 | * Example returned value: 'UTF-8'
241 | *
242 | * @return String
243 | */
244 | LaravelGettext::getEncoding();
245 | ```
246 |
247 | ```php
248 | /**
249 | * Sets the current domain
250 | *
251 | * @param String $domain
252 | */
253 | LaravelGettext::setDomain($domain);
254 | ```
255 |
256 | ```php
257 | /**
258 | * Returns the current domain
259 | *
260 | * @return String
261 | */
262 | LaravelGettext::getDomain();
263 | ```
264 |
265 | ```php
266 | /**
267 | * Returns the language selector object
268 | *
269 | * @param Array $labels
270 | * @return LanguageSelector
271 | */
272 | LaravelGettext::getSelector($labels = []);
273 | ```
274 |
275 |
276 | ### 5. Features and examples:
277 |
278 | #### A. Route and controller implementation example:
279 |
280 | app/Http/routes.php
281 |
282 | ```php
283 | Route::get('/lang/{locale?}', [
284 | 'as'=>'lang',
285 | 'uses'=>'HomeController@changeLang'
286 | ]);
287 | ```
288 |
289 | app/Http/Controllers/HomeController.php
290 |
291 | ```php
292 | /**
293 | * Changes the current language and returns to previous page
294 | * @return Redirect
295 | */
296 | public function changeLang($locale=null)
297 | {
298 | LaravelGettext::setLocale($locale);
299 | return Redirect::to(URL::previous());
300 | }
301 | ```
302 |
303 | #### B. A basic language selector example:
304 |
305 | ```php
306 |
307 | @foreach(Config::get('laravel-gettext.supported-locales') as $locale)
308 | - {{$locale}}
309 | @endforeach
310 |
311 | ```
312 |
313 | #### C. Built-in language selector:
314 |
315 | You can use the built-in language selector in your views:
316 |
317 | ```php
318 | // Plain php:
319 | LaravelGettext::getSelector()->render();
320 |
321 | // Blade views:
322 | {!! LaravelGettext::getSelector()->render() !!}
323 | ```
324 |
325 | It also supports custom labels:
326 |
327 | ```php
328 | LaravelGettext::getSelector([
329 | 'en_US' => 'English',
330 | 'es_ES' => 'Spanish',
331 | 'de_DE' => 'Deutsch',
332 | ])->render();
333 | ```
334 |
335 | #### D. Adding source directories and domains
336 |
337 | You can achieve this editing the **source-paths** configuration array. By default resources/views and app/Http/Controllers are set.
338 |
339 | ```php
340 | /**
341 | * Paths where Poedit will search recursively for strings to translate.
342 | * All paths are relative to app/ (don't use trailing slash).
343 | *
344 | * Remember to call artisan gettext:update after change this.
345 | */
346 | 'source-paths' => array(
347 | 'Http/Controllers',
348 | '../resources/views',
349 | 'foo/bar', // app/foo/bar
350 | ),
351 | ```
352 |
353 | You may want your **translations in different files**. Translations in GNUGettext are separated by domains, domains are simply context names.
354 |
355 | Laravel-Gettext set always a default domain that contains all paths that doesn't belong to any domain, its name is established by the 'domain' configuration option.
356 |
357 | To add a new domain just wrap your paths in the desired domain name, like this example:
358 |
359 | ```php
360 | 'source-paths' => array(
361 | 'frontend' => array(
362 | 'Http/Controllers',
363 | '../resources/views/frontend',
364 | ),
365 | 'backend' => array(
366 | '../resources/views/backend',
367 | ),
368 | '../resources/views/misc',
369 | ),
370 | ```
371 |
372 | This configuration generates three translation files by each language: **messages.po**, **frontend.po** and **backend.po**
373 |
374 | To change the current domain in runtime (a route-middleware would be a nice place for do this):
375 |
376 | ```php
377 | LaravelGettext::setDomain("backend");
378 | ```
379 |
380 | **Remember:** *update your gettext files every time you change the 'source-paths'* option, otherwise is not necessary.
381 |
382 | ```bash
383 | php artisan gettext:update
384 | ```
385 |
386 | This command will update your PO files and will keep the current translations intact. After this you can open Poedit and click on update button to add the new text strings in the new paths.
387 |
388 | You can update only the files of a single domain with the same command:
389 |
390 | ```bash
391 | php artisan gettext:update --domain backend
392 | ```
393 |
394 | #### E. About gettext cache (only applies to php-gettext native module)
395 |
396 | Sometimes when you edit/add translations on PO files the changes does not appear instantly. This is because the gettext cache system holds content. The most quick fix is restart your web server.
397 |
398 | ### 6. Contributing
399 |
400 | If you want to help with the development of this package, you can:
401 |
402 | - Warn about errors that you find, in issues section
403 | - Send me a pull request with your patch
404 | - Fix my disastrous English in the documentation/comments ;-)
405 | - Make a fork and create your own version of laravel-gettext
406 | - Give a star!
407 |
408 |
409 | ### 7. Upgrade from 4.*
410 | If you're upgrading from the 4.*, the one for Laravel 5.3.*, you need to refactor your usage of the ``__`` method.
411 |
412 | Laravel now use this method for their own translation. You now need to use ``_i`` instead and add this keyword in the configuration file of Laravel-Gettext:
413 |
414 | Also, if you're using Symfony as your backend, you can add the `_s` method. It's made to use the full feature set of Symfony plurals syntax.
415 |
416 | ```php
417 | /**
418 | * The keywords list used by poedit to search the strings to be translated
419 | *
420 | * The "_", "__" and "gettext" are singular translation functions
421 | * The "_n" and "ngettext" are plural translation functions
422 | * The "dgettext" function allows a translation domain to be explicitly specified
423 | *
424 | * "__" and "_n" and "_i" and "_s" are helpers functions @see \Xinax\LaravelGettext\Support\helpers.php
425 | */
426 | 'keywords-list' => ['_', '__', '_i', '_s', 'gettext', '_n:1,2', 'ngettext:1,2', 'dgettext:2'],;
427 | ```
428 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zerospam/laravel-gettext",
3 | "description": "Adds localization support to laravel applications in an easy way using Poedit and GNU gettext.",
4 | "homepage": "https://github.com/zerospam/laravel-gettext",
5 | "keywords": [
6 | "gettext",
7 | "localization",
8 | "poedit",
9 | "laravel-gettext",
10 | "laravel", "translation"
11 | ],
12 | "license": "MIT",
13 | "authors": [
14 | {
15 | "name": "Nicolás Daniel Palumbo",
16 | "email": "n@xinax.net"
17 | },
18 | {
19 | "name": "Antoine Aflalo",
20 | "email": "dev+laravel_gettext@zerospam.ca"
21 | }
22 | ],
23 | "support": {
24 | "issues": "https://github.com/zerospam/laravel-gettext/issues"
25 | },
26 | "require": {
27 | "php": ">=7.2",
28 | "laravel/framework": "^6.0 || ^7.0 || ^8.0",
29 | "laravel/helpers": "^1.1"
30 | },
31 | "require-dev": {
32 | "mockery/mockery": "dev-master",
33 | "phpunit/phpunit": "~7.0 || ^8.5 || ^9",
34 | "squizlabs/php_codesniffer" : "1.5.*",
35 | "laravel/laravel": "^6.0 || ^7.0 || ^8.0",
36 | "php-coveralls/php-coveralls": "^2.1"
37 | },
38 | "autoload": {
39 | "psr-0": {
40 | "Xinax\\LaravelGettext\\": "src/"
41 | },
42 | "files": [
43 | "src/Xinax/LaravelGettext/Support/helpers.php"
44 | ]
45 | },
46 | "minimum-stability": "stable",
47 | "extra": {
48 | "laravel": {
49 | "providers": [
50 | "Xinax\\LaravelGettext\\LaravelGettextServiceProvider"
51 | ]
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/Adapters/AdapterInterface.php:
--------------------------------------------------------------------------------
1 | fileSystem = new FileSystem(
35 | $configManager->get(),
36 | app_path(),
37 | storage_path()
38 | );
39 |
40 | $this->configuration = $configManager->get();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/Commands/GettextCreate.php:
--------------------------------------------------------------------------------
1 | prepare();
29 |
30 | // Directories created counter
31 | $dirCreations = 0;
32 |
33 | try {
34 | // Locales
35 | $localesGenerated = $this->fileSystem->generateLocales();
36 |
37 | foreach ($localesGenerated as $localePath) {
38 | $this->comment(sprintf("Locale directory created (%s)", $localePath));
39 | $dirCreations++;
40 | }
41 |
42 | $this->info("Finished");
43 |
44 | $msg = "The directory structure is right. No directory creation were needed.";
45 |
46 | if ($dirCreations) {
47 | $msg = $dirCreations . " directories has been created.";
48 | }
49 |
50 | $this->info($msg);
51 |
52 | } catch (\Exception $e) {
53 | $this->error($e->getFile() . ":" . $e->getLine() . " - " . $e->getMessage());
54 | }
55 | }
56 |
57 | /**
58 | * Get the console command arguments.
59 | *
60 | * @return array
61 | */
62 | protected function getArguments()
63 | {
64 | return [];
65 | }
66 |
67 | /**
68 | * Get the console command options.
69 | *
70 | * @return array
71 | */
72 | protected function getOptions()
73 | {
74 | return [];
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/Commands/GettextUpdate.php:
--------------------------------------------------------------------------------
1 | prepare();
34 |
35 | $domainPath = $this->fileSystem->getDomainPath();
36 | $fileSystem = $this->fileSystem;
37 |
38 | try {
39 | // Translation files base path
40 | if (!file_exists($domainPath)) {
41 | throw new DirectoryNotFoundException(
42 | "You need to call gettext:create (No locale directory)"
43 | );
44 | }
45 |
46 | $count = [
47 | 'added' => 0,
48 | 'updated' => 0,
49 | ];
50 |
51 | $domains = $this->configuration->getAllDomains();
52 |
53 | foreach ($this->configuration->getSupportedLocales() as $locale) {
54 | $localePath = $this->fileSystem->getDomainPath($locale);
55 |
56 | // Create new locale
57 | if (!file_exists($localePath)) {
58 | $this->fileSystem->addLocale($localePath, $locale);
59 | $this->comment("New locale was added: $locale ($localePath)");
60 |
61 | $count['added']++;
62 |
63 | continue;
64 | }
65 |
66 | // Domain by command line argument
67 | if ($this->option('domain')) {
68 | $domains = [$this->option('domain')];
69 | }
70 |
71 | // Update by domain(s)
72 | foreach ($domains as $domain) {
73 | $fileSystem->updateLocale(
74 | $localePath,
75 | $locale,
76 | $domain
77 | );
78 |
79 | $this->comment(
80 | sprintf(
81 | "PO file for locale: %s/%s updated successfully",
82 | $locale,
83 | $domain
84 | )
85 | );
86 |
87 | $count['updated']++;
88 | }
89 | }
90 |
91 | $this->info("Finished");
92 |
93 | // Number of locales created
94 | if ($count['added'] > 0) {
95 | $this->info(sprintf('%s new locales were added.', $count['added']));
96 | }
97 |
98 |
99 | // Number of locales updated
100 | if ($count['updated'] > 0) {
101 | $this->info(sprintf('%s locales updated.', $count['updated']));
102 | }
103 |
104 | } catch (Exception $e) {
105 | $this->error($e->getFile() . ":" . $e->getLine() . " = " . $e->getMessage());
106 | }
107 | }
108 |
109 | /**
110 | * Get the console command arguments.
111 | *
112 | * @return array
113 | */
114 | protected function getArguments()
115 | {
116 | return [];
117 | }
118 |
119 | /**
120 | * Get the console command options.
121 | *
122 | * @return array
123 | */
124 | protected function getOptions()
125 | {
126 | return [
127 | [
128 | 'domain',
129 | '-d',
130 | InputOption::VALUE_OPTIONAL,
131 | 'Update files only for this domain',
132 | null,
133 | ]
134 | ];
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/Composers/LanguageSelector.php:
--------------------------------------------------------------------------------
1 | labels = $labels;
30 | $this->gettext = $gettext;
31 | }
32 |
33 | /**
34 | * @param LaravelGettext $gettext
35 | * @param array $labels
36 | * @return LanguageSelector
37 | */
38 | public static function create(LaravelGettext $gettext, $labels = [])
39 | {
40 | return new LanguageSelector($gettext, $labels);
41 | }
42 |
43 | /**
44 | * Renders the language selector
45 | * @return string
46 | */
47 | public function render()
48 | {
49 | /** @var string $currentLocale */
50 | $currentLocale = $this->gettext->getLocale();
51 |
52 | $html = '';
53 |
54 | foreach ($this->gettext->getSupportedLocales() as $locale) {
55 | $localeLabel = $locale;
56 |
57 | // Check if label exists
58 | if (array_key_exists($locale, $this->labels)) {
59 | $localeLabel = $this->labels[$locale];
60 | }
61 |
62 |
63 | $link = '' . $localeLabel . '';
64 |
65 | if ($locale == $currentLocale) {
66 | $link = '' . $localeLabel . '';
67 | }
68 |
69 | $html .= '- ' . $link . '
';
70 | }
71 |
72 | $html .= '
';
73 |
74 | return $html;
75 | }
76 |
77 | /**
78 | * Convert to string
79 | *
80 | * @return string
81 | */
82 | public function __toString()
83 | {
84 | return $this->render();
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/Config/ConfigManager.php:
--------------------------------------------------------------------------------
1 | config = $this->generateFromArray($config);
33 | } else {
34 | // In Laravel 5.3 we need empty config model
35 | $this->config = new ConfigModel;
36 | }
37 | }
38 |
39 | /**
40 | * Get new instance of the ConfigManager
41 | *
42 | * @param null $config
43 | * @return static
44 | * @throws RequiredConfigurationFileException
45 | */
46 | public static function create($config = null)
47 | {
48 | if (is_null($config)) {
49 | // Default package configuration file (published)
50 | $config = Config::get(static::DEFAULT_PACKAGE_CONFIG);
51 | }
52 |
53 | return new static($config);
54 | }
55 |
56 | /**
57 | * Get the config model
58 | *
59 | * @return ConfigModel
60 | */
61 | public function get()
62 | {
63 | return $this->config;
64 | }
65 |
66 | /**
67 | * Creates a configuration container and checks the required fields
68 | *
69 | * @param array $config
70 | * @return ConfigModel
71 | * @throws RequiredConfigurationKeyException
72 | */
73 | protected function generateFromArray(array $config)
74 | {
75 | $requiredKeys = [
76 | 'locale',
77 | 'fallback-locale',
78 | 'encoding'
79 | ];
80 |
81 | foreach ($requiredKeys as $key) {
82 | if (!array_key_exists($key, $config)) {
83 | throw new RequiredConfigurationKeyException(
84 | sprintf('Unconfigured required value: %s', $key)
85 | );
86 | }
87 | }
88 |
89 | $container = new ConfigModel();
90 |
91 | $id = isset($config['session-identifier']) ? $config['session-identifier'] : 'laravel-gettext-locale';
92 |
93 | $adapter = isset($config['adapter']) ? $config['adapter'] : \Xinax\LaravelGettext\Adapters\LaravelAdapter::class;
94 |
95 | $storage = isset($config['storage']) ? $config['storage'] : SessionStorage::class;
96 |
97 | $container->setLocale($config['locale'])
98 | ->setSessionIdentifier($id)
99 | ->setEncoding($config['encoding'])
100 | ->setCategories(array_get('categories', $config, ['LC_ALL']))
101 | ->setFallbackLocale($config['fallback-locale'])
102 | ->setSupportedLocales($config['supported-locales'])
103 | ->setDomain($config['domain'])
104 | ->setTranslationsPath($config['translations-path'])
105 | ->setProject($config['project'])
106 | ->setTranslator($config['translator'])
107 | ->setSourcePaths($config['source-paths'])
108 | ->setSyncLaravel($config['sync-laravel'])
109 | ->setAdapter($adapter)
110 | ->setStorage($storage);
111 |
112 | if (array_key_exists('relative-path', $config)) {
113 | $container->setRelativePath($config['relative-path']);
114 | }
115 |
116 | if (array_key_exists("custom-locale", $config)) {
117 | $container->setCustomLocale($config['custom-locale']);
118 | }
119 |
120 | if (array_key_exists("keywords-list", $config)) {
121 | $container->setKeywordsList($config['keywords-list']);
122 | }
123 |
124 | if (array_key_exists("handler", $config)) {
125 | $container->setHandler($config['handler']);
126 | }
127 |
128 | return $container;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/Config/Models/Config.php:
--------------------------------------------------------------------------------
1 | encoding = 'UTF-8';
136 | $this->supportedLocales = [];
137 | $this->sourcePaths = [];
138 | $this->customLocale = false;
139 | $this->relativePath = "../../../../../app";
140 | }
141 |
142 | public function getRelativePath()
143 | {
144 | return $this->relativePath;
145 | }
146 |
147 | public function setRelativePath($path)
148 | {
149 | $this->relativePath = $path;
150 | }
151 |
152 | /**
153 | * @return string
154 | */
155 | public function getSessionIdentifier()
156 | {
157 | return $this->sessionIdentifier;
158 | }
159 |
160 | /**
161 | * @param string $sessionIdentifier
162 | *
163 | * @return $this
164 | */
165 | public function setSessionIdentifier($sessionIdentifier)
166 | {
167 | $this->sessionIdentifier = $sessionIdentifier;
168 |
169 | return $this;
170 | }
171 |
172 | /**
173 | * @return string
174 | */
175 | public function getEncoding()
176 | {
177 | return $this->encoding;
178 | }
179 |
180 | /**
181 | * @param string $encoding
182 | *
183 | * @return $this
184 | */
185 | public function setEncoding($encoding)
186 | {
187 | $this->encoding = $encoding;
188 |
189 | return $this;
190 | }
191 |
192 | /**
193 | * @return string
194 | */
195 | public function getLocale()
196 | {
197 | return $this->locale;
198 | }
199 |
200 | /**
201 | * @param string $locale
202 | *
203 | * @return $this
204 | */
205 | public function setLocale($locale)
206 | {
207 | $this->locale = $locale;
208 |
209 | return $this;
210 | }
211 |
212 | /**
213 | * Gets categories
214 | *
215 | * @return array
216 | */
217 | public function getCategories()
218 | {
219 | return $this->categories;
220 | }
221 |
222 | /**
223 | * Sets categories
224 | *
225 | * @param array $categories
226 | *
227 | * @return self
228 | */
229 | public function setCategories($categories)
230 | {
231 | $this->categories = $categories;
232 |
233 | return $this;
234 | }
235 |
236 | /**
237 | * @return string
238 | */
239 | public function getFallbackLocale()
240 | {
241 | return $this->fallbackLocale;
242 | }
243 |
244 | /**
245 | * @param string $fallbackLocale
246 | *
247 | * @return $this
248 | */
249 | public function setFallbackLocale($fallbackLocale)
250 | {
251 | $this->fallbackLocale = $fallbackLocale;
252 |
253 | return $this;
254 | }
255 |
256 | /**
257 | * @return array
258 | */
259 | public function getSupportedLocales()
260 | {
261 | return $this->supportedLocales;
262 | }
263 |
264 | /**
265 | * @param array $supportedLocales
266 | *
267 | * @return $this
268 | */
269 | public function setSupportedLocales($supportedLocales)
270 | {
271 | $this->supportedLocales = $supportedLocales;
272 |
273 | return $this;
274 | }
275 |
276 | /**
277 | * @return string
278 | */
279 | public function getDomain()
280 | {
281 | return $this->domain;
282 | }
283 |
284 | /**
285 | * @param string $domain
286 | *
287 | * @return $this
288 | */
289 | public function setDomain($domain)
290 | {
291 | $this->domain = $domain;
292 |
293 | return $this;
294 | }
295 |
296 | /**
297 | * @return string
298 | */
299 | public function getTranslationsPath()
300 | {
301 | return $this->translationsPath;
302 | }
303 |
304 | /**
305 | * @param string $translationsPath
306 | *
307 | * @return $this
308 | */
309 | public function setTranslationsPath($translationsPath)
310 | {
311 | $this->translationsPath = $translationsPath;
312 |
313 | return $this;
314 | }
315 |
316 | /**
317 | * @return string
318 | */
319 | public function getProject()
320 | {
321 | return $this->project;
322 | }
323 |
324 | /**
325 | * @param string $project
326 | *
327 | * @return $this
328 | */
329 | public function setProject($project)
330 | {
331 | $this->project = $project;
332 |
333 | return $this;
334 | }
335 |
336 | /**
337 | * @return string
338 | */
339 | public function getTranslator()
340 | {
341 | return $this->translator;
342 | }
343 |
344 | /**
345 | * @param string $translator
346 | *
347 | * @return $this
348 | */
349 | public function setTranslator($translator)
350 | {
351 | $this->translator = $translator;
352 |
353 | return $this;
354 | }
355 |
356 | /**
357 | * @return array
358 | */
359 | public function getSourcePaths()
360 | {
361 | return $this->sourcePaths;
362 | }
363 |
364 | /**
365 | * @param array $sourcePaths
366 | *
367 | * @return $this
368 | */
369 | public function setSourcePaths($sourcePaths)
370 | {
371 | $this->sourcePaths = $sourcePaths;
372 |
373 | return $this;
374 | }
375 |
376 | /**
377 | * @return boolean
378 | */
379 | public function isSyncLaravel()
380 | {
381 | return $this->syncLaravel;
382 | }
383 |
384 | /**
385 | * Gets the Sync with laravel locale.
386 | *
387 | * @return mixed
388 | */
389 | public function getSyncLaravel()
390 | {
391 | return $this->syncLaravel;
392 | }
393 |
394 | /**
395 | * @param boolean $syncLaravel
396 | *
397 | * @return $this
398 | */
399 | public function setSyncLaravel($syncLaravel)
400 | {
401 | $this->syncLaravel = $syncLaravel;
402 |
403 | return $this;
404 | }
405 |
406 | /**
407 | * Gets the adapter class.
408 | *
409 | * @return string
410 | */
411 | public function getAdapter()
412 | {
413 | return $this->adapter;
414 | }
415 |
416 | /**
417 | * @param string $adapter
418 | *
419 | * @return $this
420 | */
421 | public function setAdapter($adapter)
422 | {
423 | $this->adapter = $adapter;
424 |
425 | return $this;
426 | }
427 |
428 | /**
429 | * Getter for storage
430 | *
431 | * @return string
432 | */
433 | public function getStorage()
434 | {
435 | return $this->storage;
436 | }
437 |
438 | /**
439 | * @param string $storage
440 | *
441 | * @return $this
442 | */
443 | public function setStorage($storage)
444 | {
445 | $this->storage = $storage;
446 |
447 | return $this;
448 | }
449 |
450 |
451 |
452 | /**
453 | * Return an array with all domain names
454 | *
455 | * @return array
456 | */
457 | public function getAllDomains()
458 | {
459 | $domains = [$this->domain]; // add the default domain
460 |
461 | foreach ($this->sourcePaths as $domain => $paths) {
462 | if (is_array($paths)) {
463 | array_push($domains, $domain);
464 | }
465 | }
466 |
467 | return array_unique($domains);
468 | }
469 |
470 | /**
471 | * Return all routes for a single domain
472 | *
473 | * @param $domain
474 | *
475 | * @return array
476 | */
477 | public function getSourcesFromDomain($domain)
478 | {
479 | // grab any paths wrapped in $domain
480 | $explicitPaths = array_key_exists($domain, $this->sourcePaths)
481 | ? $this->sourcePaths[$domain]
482 | : [];
483 |
484 | // if we're not including the default domain, return what we have so far
485 | if ($this->domain != $domain) {
486 | return $explicitPaths;
487 | }
488 |
489 | // otherwise, grab all the default domain paths
490 | // and merge them with paths wrapped in $domain
491 | return array_reduce(
492 | $this->sourcePaths,
493 | function ($carry, $path) {
494 | if (!is_array($path)) {
495 | $carry[] = $path;
496 | }
497 |
498 | return $carry;
499 | },
500 | $explicitPaths
501 | );
502 | }
503 |
504 | /**
505 | * Gets C locale setting.
506 | *
507 | * @return boolean
508 | */
509 | public function getCustomLocale()
510 | {
511 | return $this->customLocale;
512 | }
513 |
514 | /**
515 | * Sets if will use C locale structure.
516 | *
517 | * @param mixed $sourcePaths the source paths
518 | *
519 | * @return self
520 | */
521 | public function setCustomLocale($customLocale)
522 | {
523 | $this->customLocale = $customLocale;
524 |
525 | return $this;
526 | }
527 |
528 | /**
529 | * Gets the Poedit keywords list.
530 | *
531 | * @return mixed
532 | */
533 | public function getKeywordsList()
534 | {
535 | return !empty($this->keywordsList)
536 | ? $this->keywordsList
537 | : ['_'];
538 | }
539 |
540 | /**
541 | * Sets the Poedit keywords list.
542 | *
543 | * @param mixed $keywordsList the keywords list
544 | *
545 | * @return self
546 | */
547 | public function setKeywordsList($keywordsList)
548 | {
549 | $this->keywordsList = $keywordsList;
550 |
551 | return $this;
552 | }
553 |
554 | /**
555 | * Sets the handler type. Also check for valid handler name
556 | *
557 | * @param $handler
558 | *
559 | * @return $this
560 | *
561 | * @throws \Exception
562 | */
563 | public function setHandler($handler)
564 | {
565 | if (!in_array($handler, [
566 | 'symfony',
567 | 'gettext',
568 | ])
569 | ) {
570 | throw new \Exception("Handler '$handler' is not supported'");
571 | };
572 |
573 | $this->handler = $handler;
574 |
575 | return $this;
576 | }
577 |
578 | /**
579 | * Returns the handler name
580 | *
581 | * @return mixed
582 | */
583 | public function getHandler()
584 | {
585 | return !empty($this->handler)
586 | ? $this->handler
587 | : 'symfony';
588 | }
589 | }
590 |
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/Exceptions/DirectoryNotFoundException.php:
--------------------------------------------------------------------------------
1 | underlyingFileLoader = $underlyingFileLoader;
30 | }
31 |
32 |
33 | /**
34 | * @param string $resource
35 | *
36 | * @return array
37 | *
38 | * @throws InvalidResourceException if stream content has an invalid format
39 | */
40 | protected function loadResource(string $resource): array
41 | {
42 | if (!extension_loaded('apcu')) {
43 | return $this->underlyingFileLoader->loadResource($resource);
44 | }
45 |
46 | return $this->cachedMessages($resource);
47 | }
48 |
49 | /**
50 | * Calculate the checksum for the file
51 | *
52 | * @param $resource
53 | *
54 | * @return string
55 | */
56 | private function checksum($resource)
57 | {
58 | return filemtime($resource) . '-' . filesize($resource);
59 | }
60 |
61 | /**
62 | * Checksum saved in cache
63 | *
64 | * @param $resource
65 | *
66 | * @return string
67 | */
68 | private function cacheChecksum($resource)
69 | {
70 | return apcu_fetch($resource . '-checksum');
71 | }
72 |
73 | /**
74 | * Set the cache checksum
75 | *
76 | * @param $resource
77 | * @param $checksum
78 | *
79 | * @return array|bool
80 | */
81 | private function setCacheChecksum($resource, $checksum)
82 | {
83 | return apcu_store($resource . '-checksum', $checksum);
84 | }
85 |
86 | /**
87 | * Return the cached messages
88 | *
89 | * @param $ressource
90 | *
91 | * @return array
92 | */
93 | private function cachedMessages($ressource)
94 | {
95 | if ($this->cacheChecksum($ressource) == ($currentChecksum = $this->checksum($ressource))) {
96 | return apcu_fetch($ressource . '-messages');
97 | }
98 |
99 | $messages = $this->underlyingFileLoader->loadResource($ressource);
100 |
101 | apcu_store($ressource . '-messages', $messages);
102 | $this->setCacheChecksum($ressource, $currentChecksum);
103 |
104 | return $messages;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/FileLoader/MoFileLoader.php:
--------------------------------------------------------------------------------
1 | readLong($stream, $isBigEndian);
65 | $count = $this->readLong($stream, $isBigEndian);
66 | $offsetId = $this->readLong($stream, $isBigEndian);
67 | $offsetTranslated = $this->readLong($stream, $isBigEndian);
68 | // sizeHashes
69 | $this->readLong($stream, $isBigEndian);
70 | // offsetHashes
71 | $this->readLong($stream, $isBigEndian);
72 |
73 | $messages = array();
74 |
75 | for ($i = 0; $i < $count; ++$i) {
76 | $pluralId = null;
77 | $translated = null;
78 |
79 | fseek($stream, $offsetId + $i * 8);
80 |
81 | $length = $this->readLong($stream, $isBigEndian);
82 | $offset = $this->readLong($stream, $isBigEndian);
83 |
84 | if ($length < 1) {
85 | continue;
86 | }
87 |
88 | fseek($stream, $offset);
89 | $singularId = fread($stream, $length);
90 |
91 | if (strpos($singularId, "\000") !== false) {
92 | list($singularId, $pluralId) = explode("\000", $singularId);
93 | }
94 |
95 | fseek($stream, $offsetTranslated + $i * 8);
96 | $length = $this->readLong($stream, $isBigEndian);
97 | $offset = $this->readLong($stream, $isBigEndian);
98 |
99 | if ($length < 1) {
100 | continue;
101 | }
102 |
103 | fseek($stream, $offset);
104 | $translated = fread($stream, $length);
105 |
106 | if (strpos($translated, "\000") !== false) {
107 | $translated = explode("\000", $translated);
108 | }
109 |
110 | $ids = array('singular' => $singularId, 'plural' => $pluralId);
111 | $item = compact('ids', 'translated');
112 |
113 | if (is_array($item['translated'])) {
114 | $messages[$item['ids']['singular']] = stripcslashes($item['translated'][0]);
115 | if (isset($item['ids']['plural'])) {
116 | $messages[$item['ids']['plural']] = stripcslashes(implode('|', $item['translated']));
117 | }
118 | } elseif (!empty($item['ids']['singular'])) {
119 | $messages[$item['ids']['singular']] = stripcslashes($item['translated']);
120 | }
121 | }
122 |
123 | fclose($stream);
124 |
125 | return array_filter($messages);
126 | }
127 |
128 | /**
129 | * Reads an unsigned long from stream respecting endianess.
130 | *
131 | * @param resource $stream
132 | * @param bool $isBigEndian
133 | *
134 | * @return int
135 | */
136 | private function readLong($stream, $isBigEndian)
137 | {
138 | $result = unpack($isBigEndian ? 'N1' : 'V1', fread($stream, 4));
139 | $result = current($result);
140 |
141 | return (int) substr($result, -8);
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/FileSystem.php:
--------------------------------------------------------------------------------
1 | configuration = $config;
56 | $this->basePath = $basePath;
57 |
58 | $this->storagePath = $storagePath;
59 | $this->storageContainer = "framework";
60 | $this->folderName = 'i18n';
61 | }
62 |
63 | /**
64 | * Build views in order to parse php files
65 | *
66 | * @param Array $viewPaths
67 | * @param String $domain
68 | *
69 | * @return Boolean status
70 | */
71 | /**
72 | * Build views in order to parse php files
73 | *
74 | * @param array $viewPaths
75 | * @param string $domain
76 | * @return bool
77 | * @throws FileCreationException
78 | */
79 | public function compileViews(array $viewPaths, $domain)
80 | {
81 | // Check the output directory
82 | $targetDir = $this->storagePath . DIRECTORY_SEPARATOR . $this->storageContainer;
83 |
84 | if (!file_exists($targetDir)) {
85 | $this->createDirectory($targetDir);
86 | }
87 |
88 | // Domain separation
89 | $domainDir = $targetDir . DIRECTORY_SEPARATOR . $domain;
90 | $this->clearDirectory($domainDir);
91 | $this->createDirectory($domainDir);
92 |
93 | foreach ($viewPaths as $path) {
94 | $path = $this->basePath . DIRECTORY_SEPARATOR . $path;
95 |
96 | if (!$realPath = realPath($path)) {
97 | throw new Exceptions\DirectoryNotFoundException("Failed to resolve $path, please check that it exists");
98 | }
99 |
100 | $fs = new \Illuminate\Filesystem\Filesystem($path);
101 | $files = $fs->allFiles($realPath);
102 |
103 | $compiler = new \Illuminate\View\Compilers\BladeCompiler($fs, $domainDir);
104 |
105 | foreach ($files as $file) {
106 | $filePath = $file->getRealPath();
107 | $compiler->setPath($filePath);
108 |
109 | $contents = $compiler->compileString($fs->get($filePath));
110 |
111 | $compiledPath = $compiler->getCompiledPath($compiler->getPath());
112 |
113 | $fs->put(
114 | $compiledPath . '.php',
115 | $contents
116 | );
117 | }
118 | }
119 |
120 | return true;
121 | }
122 |
123 | /**
124 | * Constructs and returns the full path to the translation files
125 | *
126 | * @param null $append
127 | * @return string
128 | */
129 | public function getDomainPath($append = null)
130 | {
131 | $path = [
132 | $this->basePath,
133 | $this->configuration->getTranslationsPath(),
134 | $this->folderName,
135 | ];
136 |
137 | if (!is_null($append)) {
138 | array_push($path, $append);
139 | }
140 |
141 | return implode(DIRECTORY_SEPARATOR, $path);
142 | }
143 |
144 | /**
145 | * Creates a configured .po file on $path
146 | * If PHP are not able to create the file the content will be returned instead
147 | *
148 | * @param string $path
149 | * @param string $locale
150 | * @param string $domain
151 | * @param bool|true $write
152 | * @return int|string
153 | */
154 | public function createPOFile($path, $locale, $domain, $write = true)
155 | {
156 | $project = $this->configuration->getProject();
157 | $timestamp = date("Y-m-d H:iO");
158 | $translator = $this->configuration->getTranslator();
159 | $encoding = $this->configuration->getEncoding();
160 |
161 | $relativePath = $this->configuration->getRelativePath();
162 |
163 | $keywords = implode(';', $this->configuration->getKeywordsList());
164 |
165 | $template = 'msgid ""' . "\n";
166 | $template .= 'msgstr ""' . "\n";
167 | $template .= '"Project-Id-Version: ' . $project . '\n' . "\"\n";
168 | $template .= '"POT-Creation-Date: ' . $timestamp . '\n' . "\"\n";
169 | $template .= '"PO-Revision-Date: ' . $timestamp . '\n' . "\"\n";
170 | $template .= '"Last-Translator: ' . $translator . '\n' . "\"\n";
171 | $template .= '"Language-Team: ' . $translator . '\n' . "\"\n";
172 | $template .= '"Language: ' . $locale . '\n' . "\"\n";
173 | $template .= '"MIME-Version: 1.0' . '\n' . "\"\n";
174 | $template .= '"Content-Type: text/plain; charset=' . $encoding . '\n' . "\"\n";
175 | $template .= '"Content-Transfer-Encoding: 8bit' . '\n' . "\"\n";
176 | $template .= '"X-Generator: Poedit 1.5.4' . '\n' . "\"\n";
177 | $template .= '"X-Poedit-KeywordsList: ' . $keywords . '\n' . "\"\n";
178 | $template .= '"X-Poedit-Basepath: ' . $relativePath . '\n' . "\"\n";
179 | $template .= '"X-Poedit-SourceCharset: ' . $encoding . '\n' . "\"\n";
180 |
181 | // Source paths
182 | $sourcePaths = $this->configuration->getSourcesFromDomain($domain);
183 |
184 | // Compiled views on paths
185 | if (count($sourcePaths)) {
186 |
187 | // View compilation
188 | $this->compileViews($sourcePaths, $domain);
189 | array_push($sourcePaths, $this->getStorageForDomain($domain));
190 |
191 | $i = 0;
192 |
193 | foreach ($sourcePaths as $sourcePath) {
194 | $template .= '"X-Poedit-SearchPath-' . $i . ': ' . $sourcePath . '\n' . "\"\n";
195 | $i++;
196 | }
197 |
198 | }
199 |
200 | if (!$write) {
201 | return $template . "\n";
202 | }
203 |
204 | // File creation
205 | $file = fopen($path, "w");
206 | $result = fwrite($file, $template);
207 | fclose($file);
208 |
209 | return $result;
210 | }
211 |
212 | /**
213 | * Validate if the directory can be created
214 | *
215 | * @param $path
216 | * @throws FileCreationException
217 | */
218 | protected function createDirectory($path)
219 | {
220 | if (!file_exists($path) && !mkdir($path)) {
221 | throw new FileCreationException(
222 | sprintf('Can\'t create the directory: %s', $path)
223 | );
224 | }
225 | }
226 |
227 | /**
228 | * Adds a new locale directory + .po file
229 | *
230 | * @param String $localePath
231 | * @param String $locale
232 | * @throws FileCreationException
233 | */
234 | public function addLocale($localePath, $locale)
235 | {
236 | $data = array(
237 | $localePath,
238 | "LC_MESSAGES"
239 | );
240 |
241 | if (!file_exists($localePath)) {
242 | $this->createDirectory($localePath);
243 | }
244 |
245 | if ($this->configuration->getCustomLocale()) {
246 | $data[1] = 'C';
247 |
248 | $gettextPath = implode(DIRECTORY_SEPARATOR, $data);
249 | if (!file_exists($gettextPath)) {
250 | $this->createDirectory($gettextPath);
251 | }
252 |
253 | $data[2] = 'LC_MESSAGES';
254 | }
255 |
256 | $gettextPath = implode(DIRECTORY_SEPARATOR, $data);
257 | if (!file_exists($gettextPath)) {
258 | $this->createDirectory($gettextPath);
259 | }
260 |
261 |
262 | // File generation for each domain
263 | foreach ($this->configuration->getAllDomains() as $domain) {
264 | $data[3] = $domain . ".po";
265 |
266 | $localePOPath = implode(DIRECTORY_SEPARATOR, $data);
267 |
268 | if (!$this->createPOFile($localePOPath, $locale, $domain)) {
269 | throw new FileCreationException(
270 | sprintf('Can\'t create the file: %s', $localePOPath)
271 | );
272 | }
273 |
274 | }
275 |
276 | }
277 |
278 | /**
279 | * Update the .po file headers by domain
280 | * (mainly source-file paths)
281 | *
282 | * @param $localePath
283 | * @param $locale
284 | * @param $domain
285 | * @return bool
286 | * @throws LocaleFileNotFoundException
287 | */
288 | public function updateLocale($localePath, $locale, $domain)
289 | {
290 | $data = [
291 | $localePath,
292 | "LC_MESSAGES",
293 | $domain . ".po",
294 | ];
295 |
296 | if ($this->configuration->getCustomLocale()) {
297 | $customLocale = array('C');
298 | array_splice($data, 1, 0, $customLocale);
299 | }
300 |
301 | $localePOPath = implode(DIRECTORY_SEPARATOR, $data);
302 |
303 | if (!file_exists($localePOPath) || !$localeContents = file_get_contents($localePOPath)) {
304 | throw new LocaleFileNotFoundException(
305 | sprintf('Can\'t read %s verify your locale structure', $localePOPath)
306 | );
307 | }
308 |
309 | $newHeader = $this->createPOFile(
310 | $localePOPath,
311 | $locale,
312 | $domain,
313 | false
314 | );
315 |
316 | // Header replacement
317 | $localeContents = preg_replace('/^([^#])+:?/', $newHeader, $localeContents);
318 |
319 | if (!file_put_contents($localePOPath, $localeContents)) {
320 | throw new LocaleFileNotFoundException(
321 | sprintf('Can\'t write on %s', $localePOPath)
322 | );
323 | }
324 |
325 | return true;
326 | }
327 |
328 | /**
329 | * Return the relative path from a file or directory to anothe
330 | *
331 | * @param string $from
332 | * @param string $to
333 | * @return string
334 | * @author Laurent Goussard
335 | */
336 | public function getRelativePath($from, $to)
337 | {
338 | // Compatibility fixes for Windows paths
339 | $from = is_dir($from) ? rtrim($from, '\/') . '/' : $from;
340 | $to = is_dir($to) ? rtrim($to, '\/') . '/' : $to;
341 | $from = str_replace('\\', '/', $from);
342 | $to = str_replace('\\', '/', $to);
343 |
344 | $from = explode('/', $from);
345 | $to = explode('/', $to);
346 | $relPath = $to;
347 |
348 | foreach ($from as $depth => $dir) {
349 | if ($dir !== $to[$depth]) {
350 | // Number of remaining directories
351 | $remaining = count($from) - $depth;
352 |
353 | if ($remaining > 1) {
354 | // Add traversals up to first matching directory
355 | $padLength = (count($relPath) + $remaining - 1) * -1;
356 |
357 | $relPath = array_pad(
358 | $relPath,
359 | $padLength,
360 | '..'
361 | );
362 |
363 | break;
364 | }
365 |
366 | $relPath[0] = './' . $relPath[0];
367 | }
368 |
369 | array_shift($relPath);
370 | }
371 |
372 | return implode('/', $relPath);
373 |
374 | }
375 |
376 | /**
377 | * Checks the required directory
378 | * Optionally checks each local directory, if $checkLocales is true
379 | *
380 | * @param bool|false $checkLocales
381 | * @return bool
382 | * @throws DirectoryNotFoundException
383 | */
384 | public function checkDirectoryStructure($checkLocales = false)
385 | {
386 | // Application base path
387 | if (!file_exists($this->basePath)) {
388 | throw new Exceptions\DirectoryNotFoundException(
389 | sprintf(
390 | 'Missing root path directory: %s, check the \'base-path\' key in your configuration.',
391 | $this->basePath
392 | )
393 | );
394 | }
395 |
396 | // Domain path
397 | $domainPath = $this->getDomainPath();
398 |
399 | // Translation files domain path
400 | if (!file_exists($domainPath)) {
401 | throw new Exceptions\DirectoryNotFoundException(
402 | sprintf(
403 | 'Missing base required directory: %s, remember to run \'artisan gettext:create\' the first time',
404 | $domainPath
405 | )
406 | );
407 | }
408 |
409 | if (!$checkLocales) {
410 | return true;
411 | }
412 |
413 | foreach ($this->configuration->getSupportedLocales() as $locale) {
414 | // Default locale is not needed
415 | if ($locale == $this->configuration->getLocale()) {
416 | continue;
417 | }
418 |
419 | $localePath = $this->getDomainPath($locale);
420 |
421 | if (!file_exists($localePath)) {
422 | throw new Exceptions\DirectoryNotFoundException(
423 | sprintf(
424 | 'Missing locale required directory: %s, maybe you forgot to run \'artisan gettext:update\'',
425 | $locale
426 | )
427 | );
428 | }
429 | }
430 |
431 | return true;
432 | }
433 |
434 | /**
435 | * Creates the localization directories and files by domain
436 | *
437 | * @return array
438 | * @throws FileCreationException
439 | */
440 | public function generateLocales()
441 | {
442 | // Application base path
443 | if (!file_exists($this->getDomainPath())) {
444 | $this->createDirectory($this->getDomainPath());
445 | }
446 | $localePaths = [];
447 |
448 | // Locale directories
449 | foreach ($this->configuration->getSupportedLocales() as $locale) {
450 | $localePath = $this->getDomainPath($locale);
451 |
452 | if (!file_exists($localePath)) {
453 | // Locale directory is created
454 | $this->addLocale($localePath, $locale);
455 |
456 | array_push($localePaths, $localePath);
457 |
458 | }
459 | }
460 |
461 | return $localePaths;
462 | }
463 |
464 |
465 | /**
466 | * Gets the package configuration model.
467 | *
468 | * @return Config
469 | */
470 | public function getConfiguration()
471 | {
472 | return $this->configuration;
473 | }
474 |
475 | /**
476 | * Set the package configuration model
477 | *
478 | * @param Config $configuration
479 | * @return $this
480 | */
481 | public function setConfiguration(Config $configuration)
482 | {
483 | $this->configuration = $configuration;
484 | return $this;
485 | }
486 |
487 | /**
488 | * Get the filesystem base path
489 | *
490 | * @return string
491 | */
492 | public function getBasePath()
493 | {
494 | return $this->basePath;
495 | }
496 |
497 | /**
498 | * Set the filesystem base path
499 | *
500 | * @param $basePath
501 | * @return $this
502 | */
503 | public function setBasePath($basePath)
504 | {
505 | $this->basePath = $basePath;
506 | return $this;
507 | }
508 |
509 | /**
510 | * Get the storage path
511 | *
512 | * @return string
513 | */
514 | public function getStoragePath()
515 | {
516 | return $this->storagePath;
517 | }
518 |
519 | /**
520 | * Set the storage path
521 | *
522 | * @param $storagePath
523 | * @return $this
524 | */
525 | public function setStoragePath($storagePath)
526 | {
527 | $this->storagePath = $storagePath;
528 | return $this;
529 | }
530 |
531 | /**
532 | * Get the full path for domain storage directory
533 | *
534 | * @param $domain
535 | * @return String
536 | */
537 | public function getStorageForDomain($domain)
538 | {
539 | $domainPath = $this->storagePath .
540 | DIRECTORY_SEPARATOR .
541 | $this->storageContainer .
542 | DIRECTORY_SEPARATOR .
543 | $domain;
544 |
545 | return $this->getRelativePath($this->basePath, $domainPath);
546 | }
547 |
548 | /**
549 | * Removes the directory contents recursively
550 | *
551 | * @param string $path
552 | * @return null|boolean
553 | */
554 | public static function clearDirectory($path)
555 | {
556 | if (!file_exists($path)) {
557 | return null;
558 | }
559 |
560 | $files = new RecursiveIteratorIterator(
561 | new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
562 | RecursiveIteratorIterator::CHILD_FIRST
563 | );
564 |
565 | foreach ($files as $fileinfo) {
566 | // if the file isn't a .gitignore file we should remove it.
567 | if ($fileinfo->getFilename() !== '.gitignore') {
568 | $todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
569 | $todo($fileinfo->getRealPath());
570 | }
571 | }
572 |
573 | // since the folder now contains a .gitignore we can't remove it
574 | //rmdir($path);
575 | return true;
576 | }
577 |
578 | /**
579 | * Get the folder name
580 | *
581 | * @return string
582 | */
583 | public function getFolderName()
584 | {
585 | return $this->folderName;
586 | }
587 |
588 | /**
589 | * Set the folder name
590 | *
591 | * @param $folderName
592 | */
593 | public function setFolderName($folderName)
594 | {
595 | $this->folderName = $folderName;
596 | }
597 |
598 | /**
599 | * Returns the full path for a .po/.mo file from its domain and locale
600 | *
601 | * @param $locale
602 | * @param $domain
603 | *
604 | * @param string $type
605 | *
606 | * @return string
607 | */
608 | public function makeFilePath($locale, $domain, $type = 'po')
609 | {
610 | $filePath = implode(
611 | DIRECTORY_SEPARATOR, [
612 | $locale,
613 | 'LC_MESSAGES',
614 | $domain . "." . $type
615 | ]
616 | );
617 |
618 | return $this->getDomainPath($filePath);
619 | }
620 |
621 | }
622 |
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/LaravelGettext.php:
--------------------------------------------------------------------------------
1 | translator = $gettext;
24 | }
25 |
26 | /**
27 | * Get the current encoding
28 | *
29 | * @return string
30 | */
31 | public function getEncoding()
32 | {
33 | return $this->translator->getEncoding();
34 | }
35 |
36 | /**
37 | * Set the current encoding
38 | *
39 | * @param string $encoding
40 | * @return $this
41 | */
42 | public function setEncoding($encoding)
43 | {
44 | $this->encoding = $encoding;
45 | return $this;
46 | }
47 |
48 | /**
49 | * Gets the Current locale.
50 | *
51 | * @return string
52 | */
53 | public function getLocale()
54 | {
55 | return $this->translator->getLocale();
56 | }
57 |
58 | /**
59 | * Set current locale
60 | *
61 | * @param string $locale
62 | * @return $this
63 | * @throws Exceptions\LocaleNotSupportedException
64 | * @throws \Exception
65 | */
66 | public function setLocale($locale)
67 | {
68 | if ($locale != $this->getLocale()) {
69 | $this->translator->setLocale($locale);
70 | }
71 |
72 | return $this;
73 | }
74 |
75 | /**
76 | * Get the language portion of the locale
77 | * (ex. en_GB returns en)
78 | *
79 | * @param string|null $locale
80 | * @return string|null
81 | */
82 | public function getLocaleLanguage($locale = null)
83 | {
84 | if (is_null($locale)) {
85 | $locale = $this->getLocale();
86 | }
87 |
88 | $localeArray = explode('_', $locale);
89 |
90 | if (!isset($localeArray[0])) {
91 | return null;
92 | }
93 |
94 | return $localeArray[0];
95 | }
96 |
97 | /**
98 | * Get the language selector object
99 | *
100 | * @param array $labels
101 | * @return LanguageSelector
102 | */
103 | public function getSelector($labels = [])
104 | {
105 | return LanguageSelector::create($this, $labels);
106 | }
107 |
108 | /**
109 | * Sets the current domain
110 | *
111 | * @param string $domain
112 | * @return $this
113 | */
114 | public function setDomain($domain)
115 | {
116 | $this->translator->setDomain($domain);
117 | return $this;
118 | }
119 |
120 | /**
121 | * Returns the current domain
122 | *
123 | * @return string
124 | */
125 | public function getDomain()
126 | {
127 | return $this->translator->getDomain();
128 | }
129 |
130 | /**
131 | * Translates a message with the current handler
132 | *
133 | * @param $message
134 | * @return string
135 | */
136 | public function translate($message)
137 | {
138 | return $this->translator->translate($message);
139 | }
140 |
141 | /**
142 | * Translates a plural string with the current handler
143 | *
144 | * @param $singular
145 | * @param $plural
146 | * @param $count
147 | * @return string
148 | */
149 | public function translatePlural($singular, $plural, $count)
150 | {
151 | return $this->translator->translatePlural($singular, $plural, $count);
152 | }
153 |
154 | /**
155 | * Returns the translator.
156 | *
157 | * @return TranslatorInterface
158 | */
159 | public function getTranslator()
160 | {
161 | return $this->translator;
162 | }
163 |
164 | /**
165 | * Sets the translator
166 | *
167 | * @param TranslatorInterface $translator
168 | * @return $this
169 | */
170 | public function setTranslator(TranslatorInterface $translator)
171 | {
172 | $this->translator = $translator;
173 | return $this;
174 | }
175 |
176 | /**
177 | * Returns supported locales
178 | *
179 | * @return array
180 | */
181 | public function getSupportedLocales()
182 | {
183 | return $this->translator->supportedLocales();
184 | }
185 |
186 | /**
187 | * Indicates if given locale is supported
188 | *
189 | * @return bool
190 | */
191 | public function isLocaleSupported($locale)
192 | {
193 | return $this->translator->isLocaleSupported($locale);
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/LaravelGettextServiceProvider.php:
--------------------------------------------------------------------------------
1 | publishes([
36 | __DIR__ . '/../../config/config.php' => config_path('laravel-gettext.php')
37 | ], 'config');
38 |
39 | }
40 |
41 | /**
42 | * Register the service provider.
43 | *
44 | * @return mixed
45 | */
46 | public function register()
47 | {
48 | $configuration = ConfigManager::create();
49 |
50 | $this->app->bind(
51 | AdapterInterface::class,
52 | $configuration->get()->getAdapter()
53 | );
54 |
55 | $this->app->singleton(Config::class, function($app) use ($configuration){
56 | return $configuration->get();
57 | });
58 |
59 | // Main class register
60 | $this->app->singleton(LaravelGettext::class, function (Application $app) use ($configuration) {
61 |
62 | $fileSystem = new FileSystem($configuration->get(), app_path(), storage_path());
63 | $storage = $app->make($configuration->get()->getStorage());
64 |
65 | if ('symfony' == $configuration->get()->getHandler()) {
66 | // symfony translator implementation
67 | $translator = new Translators\Symfony(
68 | $configuration->get(),
69 | $this->app->make(AdapterInterface::class),
70 | $fileSystem,
71 | $storage
72 | );
73 | } else {
74 | // GNU/Gettext php extension
75 | $translator = new Translators\Gettext(
76 | $configuration->get(),
77 | $this->app->make(AdapterInterface::class),
78 | $fileSystem,
79 | $storage
80 | );
81 | }
82 |
83 | return new LaravelGettext($translator);
84 |
85 | });
86 | $this->app->alias(LaravelGettext::class, 'laravel-gettext');
87 |
88 | // Alias
89 | $this->app->booting(function () {
90 | $aliasLoader = AliasLoader::getInstance();
91 | $aliasLoader->alias('LaravelGettext', \Xinax\LaravelGettext\Facades\LaravelGettext::class);
92 | });
93 |
94 | $this->registerCommands();
95 | }
96 |
97 | /**
98 | * Register commands
99 | */
100 | protected function registerCommands()
101 | {
102 | // Package commands
103 | $this->app->bind('xinax::gettext.create', function ($app) {
104 | return new Commands\GettextCreate();
105 | });
106 |
107 | $this->app->bind('xinax::gettext.update', function ($app) {
108 | return new Commands\GettextUpdate();
109 | });
110 |
111 | $this->commands([
112 | 'xinax::gettext.create',
113 | 'xinax::gettext.update',
114 | ]);
115 | }
116 |
117 | /**
118 | * Get the services
119 | *
120 | * @return array
121 | */
122 | public function provides()
123 | {
124 | return [
125 | 'laravel-gettext'
126 | ];
127 | }
128 | }
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/Middleware/GettextMiddleware.php:
--------------------------------------------------------------------------------
1 | configuration = $configuration;
31 | }
32 |
33 |
34 | /**
35 | * @var String
36 | */
37 | protected $domain;
38 |
39 | /**
40 | * Current locale
41 | * @type String
42 | */
43 | protected $locale;
44 |
45 | /**
46 | * Current encoding
47 | * @type String
48 | */
49 | protected $encoding;
50 |
51 |
52 | /**
53 | * Getter for domain
54 | *
55 | * @return String
56 | */
57 | public function getDomain()
58 | {
59 | return $this->domain ?: $this->configuration->getDomain();
60 | }
61 |
62 | /**
63 | * @param String $domain
64 | *
65 | * @return $this
66 | */
67 | public function setDomain($domain)
68 | {
69 | $this->domain = $domain;
70 |
71 | return $this;
72 | }
73 |
74 | /**
75 | * Getter for locale
76 | *
77 | * @return String
78 | */
79 | public function getLocale()
80 | {
81 | return $this->locale ?: $this->configuration->getLocale();
82 | }
83 |
84 | /**
85 | * @param String $locale
86 | *
87 | * @return $this
88 | */
89 | public function setLocale($locale)
90 | {
91 | $this->locale = $locale;
92 |
93 | return $this;
94 | }
95 |
96 | /**
97 | * Getter for configuration
98 | *
99 | * @return \Xinax\LaravelGettext\Config\Models\Config
100 | */
101 | public function getConfiguration()
102 | {
103 | return $this->configuration;
104 | }
105 |
106 | /**
107 | * Getter for encoding
108 | *
109 | * @return String
110 | */
111 | public function getEncoding()
112 | {
113 | return $this->encoding ?: $this->configuration->getEncoding();
114 | }
115 |
116 | /**
117 | * @param String $encoding
118 | *
119 | * @return $this
120 | */
121 | public function setEncoding($encoding)
122 | {
123 | $this->encoding = $encoding;
124 |
125 | return $this;
126 | }
127 |
128 |
129 |
130 |
131 |
132 | }
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/Storages/SessionStorage.php:
--------------------------------------------------------------------------------
1 | configuration = $configuration;
30 | }
31 |
32 |
33 | /**
34 | * Getter for domain
35 | *
36 | * @return String
37 | */
38 | public function getDomain()
39 | {
40 | return $this->sessionGet('domain', $this->configuration->getDomain());
41 | }
42 |
43 | /**
44 | * @param String $domain
45 | *
46 | * @return $this
47 | */
48 | public function setDomain($domain)
49 | {
50 | $this->sessionSet('domain', $domain);
51 |
52 | return $this;
53 | }
54 |
55 | /**
56 | * Getter for locale
57 | *
58 | * @return String
59 | */
60 | public function getLocale()
61 | {
62 | return $this->sessionGet('locale', $this->configuration->getLocale());
63 | }
64 |
65 | /**
66 | * @param String $locale
67 | *
68 | * @return $this
69 | */
70 | public function setLocale($locale)
71 | {
72 | $this->sessionSet('locale', $locale);
73 |
74 | return $this;
75 | }
76 |
77 | /**
78 | * Getter for configuration
79 | *
80 | * @return \Xinax\LaravelGettext\Config\Models\Config
81 | */
82 | public function getConfiguration()
83 | {
84 | return $this->configuration;
85 | }
86 |
87 |
88 | /**
89 | * Return a value from session with an optional default
90 | *
91 | * @param $key
92 | * @param null $default
93 | *
94 | * @return mixed
95 | */
96 | protected function sessionGet($key, $default = null)
97 | {
98 | $token = $this->configuration->getSessionIdentifier() . "-" . $key;
99 |
100 | return Session::get($token, $default);
101 | }
102 |
103 | /**
104 | * Sets a value in session session
105 | *
106 | * @param $key
107 | * @param $value
108 | *
109 | * @return mixed
110 | */
111 | protected function sessionSet($key, $value)
112 | {
113 | $token = $this->configuration->getSessionIdentifier() . "-" . $key;
114 | Session::put($token, $value);
115 |
116 | return $this;
117 | }
118 |
119 | /**
120 | * Getter for locale
121 | *
122 | * @return String
123 | */
124 | public function getEncoding()
125 | {
126 | return $this->sessionGet('encoding', $this->configuration->getEncoding());
127 | }
128 |
129 | /**
130 | * @param string $encoding
131 | *
132 | * @return $this
133 | */
134 | public function setEncoding($encoding)
135 | {
136 | $this->sessionSet('encoding', $encoding);
137 |
138 | return $this;
139 | }
140 | }
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/Storages/Storage.php:
--------------------------------------------------------------------------------
1 | translate($message);
20 |
21 | if (strlen($translation)) {
22 | if (isset($args)) {
23 | if (!is_array($args)) {
24 | $args = array_slice(func_get_args(), 1);
25 | }
26 | $translation = vsprintf($translation, $args);
27 | }
28 |
29 | return $translation;
30 | }
31 |
32 | /**
33 | * If translations are missing returns
34 | * the original message.
35 | *
36 | * @see https://github.com/symfony/symfony/issues/13483
37 | */
38 | return $message;
39 | }
40 | }
41 |
42 | if (!function_exists('__')) {
43 | /**
44 | * Translate a formatted string based on printf formats
45 | * Can be use an array on args or use the number of the arguments
46 | *
47 | * @param string $message the message to translate
48 | * @param array|mixed $args the tokens values used inside the $message
49 | *
50 | * @return string the message translated and formatted
51 | */
52 | function __($message, $args = null)
53 | {
54 | return _i($message, $args);
55 | }
56 | }
57 |
58 | if (!function_exists('_')) {
59 | /**
60 | * Generic translation function
61 | *
62 | * @param $message
63 | *
64 | * @return mixed
65 | */
66 | function _($message, $args = null)
67 | {
68 | return _i($message, $args);
69 | }
70 | }
71 |
72 | if (!function_exists('_n')) {
73 | /**
74 | * Translate a formatted pluralized string based on printf formats
75 | * Can be use an array on args or use the number of the arguments
76 | *
77 | * @param string $singular the singular message to be translated
78 | * @param string $plural the plural message to be translated if the $count > 1
79 | * @param int $count the number of occurrence to be used to pluralize the $singular
80 | * @param array|mixed $args the tokens values used inside $singular or $plural
81 | *
82 | * @return string the message translated, pluralized and formatted
83 | */
84 | function _n($singular, $plural, $count, $args = null)
85 | {
86 | $translator = app(LaravelGettext::class);
87 | $message = $translator->translatePlural($singular, $plural, $count);
88 |
89 | if (isset($args) && !is_array($args)) {
90 | $args = array_slice(func_get_args(), 3);
91 | }
92 | $message = vsprintf($message, $args);
93 |
94 | return $message;
95 | }
96 | }
97 |
98 | if (!function_exists('_s')) {
99 | /**
100 | * Translate a formatted pluralized string based on printf formats mixed with the Symfony format
101 | * Can be use an array on args or use the number of the arguments
102 | *
103 | * Only works if Symfony is the used backend
104 | *
105 | * @param string $message The one line message containing the different pluralization separated by pipes
106 | * See Symfony translation documentation
107 | * @param int $count the number of occurrence to be used to pluralize the $singular
108 | * @param array|mixed $args the tokens values used inside $singular or $plural
109 | *
110 | * @return string the message translated, pluralized and formatted
111 | */
112 | function _s($message, $count, $args = null)
113 | {
114 | $translator = app(LaravelGettext::class);
115 | $message = $translator->getTranslator()->translatePluralInline($message, $count);
116 |
117 | if (isset($args) && !is_array($args)) {
118 | $args = array_slice(func_get_args(), 3);
119 | }
120 | $message = vsprintf($message, $args);
121 |
122 | return $message;
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/Testing/Adapter/TestAdapter.php:
--------------------------------------------------------------------------------
1 | locale;
28 | }
29 |
30 | /**
31 | * Sets the locale on the adapter
32 | *
33 | * @param string $locale
34 | *
35 | * @return boolean
36 | */
37 | public function setLocale($locale)
38 | {
39 | $this->locale = $locale;
40 |
41 | return true;
42 | }
43 |
44 | /**
45 | * Get the application path
46 | *
47 | * @return string
48 | */
49 | public function getApplicationPath()
50 | {
51 | return app_path();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/Testing/BaseTestCase.php:
--------------------------------------------------------------------------------
1 | appPath) {
30 | return null;
31 | }
32 |
33 | $app = require $this->appPath;
34 | $app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
35 |
36 | $app->register(LaravelGettextServiceProvider::class);
37 |
38 | return $app;
39 | }
40 | }
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/Translators/BaseTranslator.php:
--------------------------------------------------------------------------------
1 | configuration = $config;
52 | $this->adapter = $adapter;
53 | $this->fileSystem = $fileSystem;
54 | $this->storage = $storage;
55 | }
56 |
57 | /**
58 | * Returns the current locale string identifier
59 | *
60 | * @return String
61 | */
62 | public function getLocale()
63 | {
64 | return $this->storage->getLocale();
65 | }
66 |
67 | /**
68 | * Sets and stores on session the current locale code
69 | *
70 | * @param $locale
71 | *
72 | * @return BaseTranslator
73 | */
74 | public function setLocale($locale)
75 | {
76 | if ($locale == $this->storage->getLocale()) {
77 | return $this;
78 | }
79 |
80 | $this->storage->setLocale($locale);
81 |
82 | return $this;
83 | }
84 |
85 | /**
86 | * Returns a boolean that indicates if $locale
87 | * is supported by configuration
88 | *
89 | * @param $locale
90 | *
91 | * @return bool
92 | */
93 | public function isLocaleSupported($locale)
94 | {
95 | if ($locale) {
96 | return in_array($locale, $this->configuration->getSupportedLocales());
97 | }
98 |
99 | return false;
100 | }
101 |
102 | /**
103 | * Return the current locale
104 | *
105 | * @return mixed
106 | */
107 | public function __toString()
108 | {
109 | return $this->getLocale();
110 | }
111 |
112 | /**
113 | * Gets the Current encoding.
114 | *
115 | * @return mixed
116 | */
117 | public function getEncoding()
118 | {
119 | return $this->storage->getEncoding();
120 | }
121 |
122 | /**
123 | * Sets the Current encoding.
124 | *
125 | * @param mixed $encoding the encoding
126 | *
127 | * @return self
128 | */
129 | public function setEncoding($encoding)
130 | {
131 | $this->storage->setEncoding($encoding);
132 |
133 | return $this;
134 | }
135 |
136 | /**
137 | * Sets the current domain and updates gettext domain application
138 | *
139 | * @param String $domain
140 | *
141 | * @throws UndefinedDomainException If domain is not defined
142 | * @return self
143 | */
144 | public function setDomain($domain)
145 | {
146 | if (!in_array($domain, $this->configuration->getAllDomains())) {
147 | throw new UndefinedDomainException("Domain '$domain' is not registered.");
148 | }
149 |
150 | $this->storage->setDomain($domain);
151 |
152 | return $this;
153 | }
154 |
155 | /**
156 | * Returns the current domain
157 | *
158 | * @return String
159 | */
160 | public function getDomain()
161 | {
162 | return $this->storage->getDomain();
163 | }
164 |
165 |
166 | /**
167 | * Returns supported locales
168 | *
169 | * @return array
170 | */
171 | public function supportedLocales()
172 | {
173 | return $this->configuration->getSupportedLocales();
174 | }
175 |
176 | }
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/Translators/Gettext.php:
--------------------------------------------------------------------------------
1 | domain = $this->storage->getDomain();
69 |
70 | // Encoding is set from configuration
71 | $this->encoding = $this->storage->getEncoding();
72 |
73 | // Categories are set from configuration
74 | $this->categories = $this->configuration->getCategories();
75 |
76 | // Sets defaults for boot
77 | $locale = $this->storage->getLocale();
78 |
79 | $this->setLocale($locale);
80 | }
81 |
82 |
83 | /**
84 | * Sets the current locale code
85 | */
86 | public function setLocale($locale)
87 | {
88 | if (!$this->isLocaleSupported($locale)) {
89 | throw new LocaleNotSupportedException(
90 | sprintf('Locale %s is not supported', $locale)
91 | );
92 | }
93 |
94 | try {
95 | $customLocale = $this->configuration->getCustomLocale() ? "C." : $locale . ".";
96 | $gettextLocale = $customLocale . $this->getEncoding();
97 |
98 | // Update all categories set in config
99 | foreach($this->categories as $category) {
100 | putenv("$category=$gettextLocale");
101 | setlocale(constant($category), $gettextLocale);
102 | }
103 |
104 | parent::setLocale($locale);
105 |
106 | // Laravel built-in locale
107 | if ($this->configuration->isSyncLaravel()) {
108 | $this->adapter->setLocale($locale);
109 | }
110 |
111 | return $this->getLocale();
112 | } catch (\Exception $e) {
113 | $this->locale = $this->configuration->getFallbackLocale();
114 | $exceptionPosition = $e->getFile() . ":" . $e->getLine();
115 | throw new \Exception($exceptionPosition . $e->getMessage());
116 |
117 | }
118 | }
119 |
120 | /**
121 | * Returns a boolean that indicates if $locale
122 | * is supported by configuration
123 | *
124 | * @return boolean
125 | */
126 | public function isLocaleSupported($locale)
127 | {
128 | if ($locale) {
129 | return in_array($locale, $this->supportedLocales());
130 | }
131 |
132 | return false;
133 | }
134 |
135 | /**
136 | * Return the current locale
137 | *
138 | * @return mixed
139 | */
140 | public function __toString()
141 | {
142 | return $this->getLocale();
143 | }
144 |
145 |
146 | /**
147 | * Gets the Current encoding.
148 | *
149 | * @return mixed
150 | */
151 | public function getEncoding()
152 | {
153 | return $this->encoding;
154 | }
155 |
156 | /**
157 | * Sets the Current encoding.
158 | *
159 | * @param mixed $encoding the encoding
160 | * @return self
161 | */
162 | public function setEncoding($encoding)
163 | {
164 | $this->encoding = $encoding;
165 | return $this;
166 | }
167 |
168 | /**
169 | * Sets the current domain and updates gettext domain application
170 | *
171 | * @param String $domain
172 | * @throws UndefinedDomainException If domain is not defined
173 | * @return self
174 | */
175 | public function setDomain($domain)
176 | {
177 | parent::setDomain($domain);
178 |
179 | $customLocale = $this->configuration->getCustomLocale() ? "/" . $this->getLocale() : "";
180 |
181 | bindtextdomain($domain, $this->fileSystem->getDomainPath() . $customLocale);
182 | bind_textdomain_codeset($domain, $this->getEncoding());
183 |
184 | $this->domain = textdomain($domain);
185 |
186 |
187 |
188 | return $this;
189 | }
190 |
191 | /**
192 | * Translates a message with gettext
193 | *
194 | * @param $message
195 | */
196 | public function translate($message)
197 | {
198 | return gettext($message);
199 | }
200 |
201 | /**
202 | * Translates a plural message with gettext
203 | *
204 | * @param $singular
205 | * @param $plural
206 | * @param $count
207 | *
208 | * @return string
209 | */
210 | public function translatePlural($singular, $plural, $count)
211 | {
212 | return ngettext($singular, $plural, $count);
213 | }
214 |
215 | public function translatePluralInline($message, $amount)
216 | {
217 | throw new \RuntimeException('Not supported by gettext, please use Symfony');
218 | }
219 | }
220 |
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/Translators/Symfony.php:
--------------------------------------------------------------------------------
1 | setLocale($this->storage->getLocale());
36 | $this->loadLocaleFile();
37 | }
38 |
39 |
40 | /**
41 | * Translates a message using the Symfony translation component
42 | *
43 | * @param $message
44 | *
45 | * @return string
46 | */
47 | public function translate($message)
48 | {
49 | return $this->symfonyTranslator->trans($message, [], $this->getDomain(), $this->getLocale());
50 | }
51 |
52 | /**
53 | * Returns the translator instance
54 | *
55 | * @return SymfonyTranslator
56 | */
57 | protected function getTranslator()
58 | {
59 | if (isset($this->symfonyTranslator)) {
60 | return $this->symfonyTranslator;
61 | }
62 |
63 | return $this->symfonyTranslator = $this->createTranslator();
64 | }
65 |
66 | /**
67 | * Set locale overload.
68 | * Needed to re-build the catalogue when locale changes.
69 | *
70 | * @param $locale
71 | *
72 | * @return $this
73 | */
74 | public function setLocale($locale)
75 | {
76 | parent::setLocale($locale);
77 | $this->getTranslator()->setLocale($locale);
78 | $this->loadLocaleFile();
79 |
80 | if ($locale != $this->adapter->getLocale()) {
81 | $this->adapter->setLocale($locale);
82 | }
83 |
84 | return $this;
85 | }
86 |
87 | /**
88 | * Set domain overload.
89 | * Needed to re-build the catalogue when domain changes.
90 | *
91 | *
92 | * @param String $domain
93 | *
94 | * @return $this
95 | */
96 | public function setDomain($domain)
97 | {
98 | parent::setDomain($domain);
99 |
100 | $this->loadLocaleFile();
101 |
102 | return $this;
103 | }
104 |
105 | /**
106 | * Creates a new translator instance
107 | *
108 | * @return SymfonyTranslator
109 | */
110 | protected function createTranslator()
111 | {
112 | $translator = new SymfonyTranslator($this->configuration->getLocale());
113 | $translator->setFallbackLocales([$this->configuration->getFallbackLocale()]);
114 | $translator->addLoader('mo', new ApcuFileCacheLoader(new MoFileLoader()));
115 | $translator->addLoader('po', new ApcuFileCacheLoader(new PoFileLoader()));
116 |
117 | return $translator;
118 | }
119 |
120 | /**
121 | * Translates a plural string
122 | *
123 | * @param $singular
124 | * @param $plural
125 | * @param $amount
126 | *
127 | * @return string
128 | */
129 | public function translatePlural($singular, $plural, $amount)
130 | {
131 | return $this->symfonyTranslator->trans(
132 | $amount > 1
133 | ? $plural
134 | : $singular,
135 | ['%count%' => $amount],
136 | $this->getDomain(),
137 | $this->getLocale()
138 | );
139 | }
140 |
141 | /**
142 | * Translate a plural string that is only on one line separated with pipes
143 | *
144 | * @param $message
145 | * @param $amount
146 | *
147 | * @return string
148 | */
149 | public function translatePluralInline($message, $amount)
150 | {
151 | return $this->symfonyTranslator->trans(
152 | $message,
153 | [
154 | '%count%' => $amount
155 | ],
156 | $this->getDomain(),
157 | $this->getLocale()
158 | );
159 | }
160 |
161 | /**
162 | * @internal param $translator
163 | */
164 | protected function loadLocaleFile()
165 | {
166 | if (isset($this->loadedResources[$this->getDomain()])
167 | && isset($this->loadedResources[$this->getDomain()][$this->getLocale()])
168 | ) {
169 | return;
170 | }
171 | $translator = $this->getTranslator();
172 |
173 | $fileMo = $this->fileSystem->makeFilePath($this->getLocale(), $this->getDomain(), 'mo');
174 | if (file_exists($fileMo)) {
175 | $translator->addResource('mo', $fileMo, $this->getLocale(), $this->getDomain());
176 | } else {
177 | $file = $this->fileSystem->makeFilePath($this->getLocale(), $this->getDomain());
178 | $translator->addResource('po', $file, $this->getLocale(), $this->getDomain());
179 | }
180 |
181 | $this->loadedResources[$this->getDomain()][$this->getLocale()] = true;
182 | }
183 |
184 | /**
185 | * Returns a boolean that indicates if $locale
186 | * is supported by configuration
187 | *
188 | * @param $locale
189 | *
190 | * @return bool
191 | */
192 | public function isLocaleSupported($locale)
193 | {
194 | if ($locale) {
195 | return in_array($locale, $this->configuration->getSupportedLocales());
196 | }
197 |
198 | return false;
199 | }
200 |
201 | /**
202 | * Return the current locale
203 | *
204 | * @return mixed
205 | */
206 | public function __toString()
207 | {
208 | return $this->getLocale();
209 | }
210 |
211 | /**
212 | * Returns supported locales
213 | *
214 | * @return array
215 | */
216 | public function supportedLocales()
217 | {
218 | return $this->configuration->getSupportedLocales();
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/src/Xinax/LaravelGettext/Translators/TranslatorInterface.php:
--------------------------------------------------------------------------------
1 | 'symfony',
15 |
16 | /**
17 | * Session identifier: Key under which the current locale will be stored.
18 | */
19 | 'session-identifier' => 'laravel-gettext-locale',
20 |
21 | /**
22 | * Default locale: this will be the default for your application.
23 | * Is to be supposed that all strings are written in this language.
24 | */
25 | 'locale' => 'en_US',
26 |
27 | /**
28 | * Supported locales: An array containing all allowed languages
29 | */
30 | 'supported-locales' => [
31 | 'en_US',
32 | ],
33 |
34 | /**
35 | * Default charset encoding.
36 | */
37 | 'encoding' => 'UTF-8',
38 |
39 | /**
40 | * -----------------------------------------------------------------------
41 | * All standard configuration ends here. The following values
42 | * are only for special cases.
43 | * -----------------------------------------------------------------------
44 | **/
45 |
46 | /**
47 | * Locale categories to set
48 | */
49 | 'categories' => [
50 | 'LC_ALL',
51 | ],
52 |
53 | /**
54 | * Base translation directory path (don't use trailing slash)
55 | */
56 | 'translations-path' => '../resources/lang',
57 |
58 | /**
59 | * Relative path to the app folder: is used on .po header files
60 | */
61 | 'relative-path' => '../../../../../app',
62 |
63 | /**
64 | * Fallback locale: When default locale is not available
65 | */
66 | 'fallback-locale' => 'en_US',
67 |
68 | /**
69 | * Default domain used for translations: It is the file name for .po and .mo files
70 | */
71 | 'domain' => 'messages',
72 |
73 | /**
74 | * Project name: is used on .po header files
75 | */
76 | 'project' => 'MultilanguageLaravelApplication',
77 |
78 | /**
79 | * Translator contact data (used on .po headers too)
80 | */
81 | 'translator' => 'James Translator ',
82 |
83 | /**
84 | * Paths where Poedit will search recursively for strings to translate.
85 | * All paths are relative to app/ (don't use trailing slash).
86 | *
87 | * Remember to call artisan gettext:update after change this.
88 | */
89 | 'source-paths' => [
90 | 'Http',
91 | '../resources/views',
92 | 'Console',
93 | ],
94 |
95 | /**
96 | * Multi-domain directory paths. If you want the translations in
97 | * different files, just wrap your paths into a domain name.
98 | * for example:
99 | */
100 | /*
101 | 'source-paths' => [
102 |
103 | // 'frontend' domain
104 | 'frontend' => [
105 | 'controllers',
106 | 'views/frontend',
107 | ],
108 |
109 | // 'backend' domain
110 | 'backend' => [
111 | 'views/backend',
112 | ],
113 |
114 | // 'messages' domain (matches default domain)
115 | 'storage/views',
116 | ],
117 | */
118 |
119 | /**
120 | * Sync laravel: A flag that determines if the laravel built-in locale must
121 | * be changed when you call LaravelGettext::setLocale.
122 | */
123 | 'sync-laravel' => true,
124 |
125 | /**
126 | * The adapter used to sync the laravel built-in locale
127 | */
128 | 'adapter' => \Xinax\LaravelGettext\Adapters\LaravelAdapter::class,
129 |
130 | /**
131 | * Where to store the current locale/domain
132 | *
133 | * By default, in the session.
134 | * Can be changed for only memory or your own storage mechanism
135 | *
136 | * @see \Xinax\LaravelGettext\Storages\Storage
137 | */
138 | 'storage' => \Xinax\LaravelGettext\Storages\SessionStorage::class,
139 |
140 | /**
141 | * Use custom locale that is not supported by the system
142 | */
143 | 'custom-locale' => false,
144 |
145 | /**
146 | * The keywords list used by poedit to search the strings to be translated
147 | *
148 | * The "_", "__" and "gettext" are singular translation functions
149 | * The "_n" and "ngettext" are plural translation functions
150 | * The "dgettext" function allows a translation domain to be explicitly specified
151 | *
152 | * "__" and "_n" and "_i" and "_s" are helpers functions @see \Xinax\LaravelGettext\Support\helpers.php
153 | */
154 | 'keywords-list' => ['_', '__', '_i', '_s', 'gettext', '_n:1,2', 'ngettext:1,2', 'dgettext:2'],
155 | ];
156 |
--------------------------------------------------------------------------------