Completed
Push — master ( 0fab66...c72de8 )
by Alberto
13s
created

I18nHelper::newLangUrl()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 3
nop 3
dl 0
loc 13
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * BEdita, API-first content management framework
4
 * Copyright 2018 ChannelWeb Srl, Chialab Srl
5
 *
6
 * This file is part of BEdita: you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
12
 */
13
namespace BEdita\I18n\View\Helper;
14
15
use Cake\Core\Configure;
16
use Cake\I18n\I18n;
17
use Cake\Routing\Router;
18
use Cake\Utility\Hash;
19
use Cake\View\Helper;
20
21
/**
22
 * Helper to handle i18n things in view.
23
 */
24
class I18nHelper extends Helper
25
{
26
    /**
27
     * Translation data per object and lang (internal cache).
28
     * If `null` no cache has been created, if empty array no translations
29
     * have been found.
30
     *
31
     * Structure:
32
     *
33
     *   translation[<object ID>][<lang>][<field>] = <value>.
34
     *
35
     *
36
     * @var array|null
37
     */
38
    protected $translation = null;
39
40
    /**
41
     * Proxy to `\Cake\I18n\I18n::getLocale()`.
42
     * Return the currently configure locale as stored in the `intl.default_locale` PHP setting.
43
     *
44
     * @return string The name of the default locale.
45
     *
46
     * @codeCoverageIgnore
47
     */
48
    public function getLocale() : string
49
    {
50
        return I18n::getLocale();
51
    }
52
53
    /**
54
     * Return an array of available languages.
55
     *
56
     * @return array
57
     *
58
     * @codeCoverageIgnore
59
     */
60
    public function getLanguages() : array
61
    {
62
        return (array)Configure::read('I18n.languages');
63
    }
64
65
    /**
66
     * Return the current lang usually set by `\BEdita\I18n\Middleware\I18nMiddleware`
67
     *
68
     * @return string|null
69
     *
70
     * @codeCoverageIgnore
71
     */
72
    public function getLang() : ?string
73
    {
74
        return Configure::read('I18n.lang');
75
    }
76
77
    /**
78
     * Return the language name as configured.
79
     *
80
     * @param string $lang The abbreviated lang
81
     * @return string|null
82
     */
83
    public function getLangName($lang = null) : ?string
84
    {
85
        if (empty($lang)) {
86
            $lang = Configure::read('I18n.default');
87
        }
88
89
        return Hash::get($this->getLanguages(), $lang);
90
    }
91
92
    /**
93
     * Return the current URL replacing current lang with new lang passed.
94
     *
95
     * @param string $newLang The new lang you want in URL.
96
     * @param string $switchUrl The switch lang URL defined for this app, if any.
97
     * @return string
98
     */
99
    public function changeUrlLang($newLang, $switchUrl = null) : string
100
    {
101
        $request = Router::getRequest(true);
102
        if (empty($request)) {
103
            return '';
104
        }
105
        $path = $request->getUri()->getPath();
106
        $query = $request->getUri()->getQuery();
107
108
        $newLangUrl = $this->newLangUrl($newLang, $path, $query);
109
        if ($newLangUrl !== null) {
110
            return $newLangUrl;
111
        }
112
113
        if (!empty($switchUrl)) {
114
            return sprintf('%s?new=%s', $switchUrl, $newLang);
115
        }
116
117
        if (!empty($query)) {
118
            $path .= sprintf('?%s', $query);
119
        }
120
121
        return $path;
122
    }
123
124
    /**
125
     * Try to create a new language URL from current path using lang prefix.
126
     *
127
     * @param string $newLang The new lang you want in URL.
128
     * @param string $path The current URL path.
129
     * @param string $query The current URL query.
130
     * @return string|null The new lang url or null if no lang prefix was found
131
     */
132
    protected function newLangUrl($newLang, $path, $query) : ?string
133
    {
134
        $prefix = sprintf('/%s', $this->getLang());
135
        if (stripos($path, $prefix . '/') === 0 || $path === $prefix) {
136
            $url = sprintf('/%s', $newLang) . substr($path, strlen($prefix));
137
            if ($query) {
138
                $url .= '?' . $query;
139
            }
140
141
            return $url;
142
        }
143
144
        return null;
145
    }
146
147
    /**
148
     * Translate object field
149
     * Return translation (by response object and included data, field and language)
150
     *
151
     * @param array $object The object to translate
152
     * @param string $attribute The attribute name
153
     * @param string|null $lang The lang (2 chars string)
154
     * @param bool $defaultNull Pass true when you want null as default, on missing translation
155
     * @param array $included The included translations data
156
     * @return string|null
157
     */
158
    public function field(array $object, string $attribute, ?string $lang = null, bool $defaultNull = false, array &$included = []) : ?string
159
    {
160
        $defaultValue = null;
161
        if (!$defaultNull) {
162
            $defaultValue = Hash::get($object, sprintf('attributes.%s', $attribute), Hash::get($object, sprintf('%s', $attribute)));
163
        }
164
        if (empty($included) && !empty($this->getView()->viewVars['included'])) {
165
            $included = $this->getView()->viewVars['included'];
166
        }
167
        if (empty($lang)) {
168
            $lang = Configure::read('I18n.lang', '');
169
        }
170
        $returnValue = $this->getTranslatedField($object, $attribute, $lang, $included);
171
        if ($returnValue === null) {
172
            return $defaultValue;
173
        }
174
175
        return $returnValue;
176
    }
177
178
    /**
179
     * Verify that object has translation for the specified attribute and lang
180
     *
181
     * @param array $object The object to translate
182
     * @param string $attribute The attribute name
183
     * @param string|null $lang The lang (2 chars string
184
     * @param array $included The included translations data)
185
     * @return bool
186
     */
187
    public function exists(array $object, string $attribute, ?string $lang = null, array &$included = []) : bool
188
    {
189
        if (empty($included) && !empty($this->getView()->viewVars['included'])) {
190
            $included = $this->getView()->viewVars['included'];
191
        }
192
        if (empty($lang)) {
193
            $lang = Configure::read('I18n.lang', '');
194
        }
195
        $val = $this->getTranslatedField($object, $attribute, $lang, $included);
196
197
        return ($val !== null);
198
    }
199
200
    /**
201
     * Reset internal translation cache.
202
     * To use when `included` array has changed.
203
     *
204
     * @return void
205
     */
206
    public function reset() : void
207
    {
208
        $this->translation = null;
209
    }
210
211
    /**
212
     * Return translated field per response object and included, attribute and lang. Null on missing translation.
213
     * First time that it's called per response object and included, it fills $this->translation data.
214
     * I.e.:
215
     *
216
     *     $this->translation[100]['en'] = ['title' => 'Example', 'description' => 'This is an example']
217
     *     $this->translation[100]['it'] = ['title' => 'Esempio', 'description' => 'Questo è un esempio']
218
     *     $this->translation[100]['sp'] = ['title' => 'Ejemplo', 'description' => 'Este es un ejemplo']
219
     *
220
     * @param array $object The object to translate
221
     * @param string $attribute The attribute name
222
     * @param string $lang The lang (2 chars string)
223
     * @param array $included The included translations data
224
     * @return string|null The translation of attribute field per object response and lang
225
     */
226
    private function getTranslatedField(array $object, string $attribute, string $lang, array &$included) : ?string
227
    {
228
        if (empty($object['id'])) {
229
            return null;
230
        }
231
232
        $id = $object['id'];
233
234
        if ($this->translation === null) {
235
            $translations = Hash::combine($included, '{n}.id', '{n}.attributes', '{n}.type');
236
            $this->translation = Hash::combine(
237
                $translations,
238
                'translations.{n}.lang',
239
                'translations.{n}.translated_fields',
240
                'translations.{n}.object_id'
241
            );
242
        }
243
244
        $path = sprintf('%s.%s.%s', $id, $lang, $attribute);
245
246
        return Hash::get($this->translation, $path);
247
    }
248
}
249