Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v4] New translations root #6216

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 27 additions & 26 deletions src/Cms/Core.php
Original file line number Diff line number Diff line change
Expand Up @@ -316,33 +316,34 @@ public function load(): Loader
public function roots(): array
{
return $this->cache['roots'] ??= [
'kirby' => fn (array $roots) => dirname(__DIR__, 2),
'i18n' => fn (array $roots) => $roots['kirby'] . '/i18n',
'kirby' => fn (array $roots) => dirname(__DIR__, 2),
'i18n' => fn (array $roots) => $roots['kirby'] . '/i18n',
'i18n:translations' => fn (array $roots) => $roots['i18n'] . '/translations',
'i18n:rules' => fn (array $roots) => $roots['i18n'] . '/rules',

'index' => fn (array $roots) => static::$indexRoot ?? dirname(__DIR__, 3),
'assets' => fn (array $roots) => $roots['index'] . '/assets',
'content' => fn (array $roots) => $roots['index'] . '/content',
'media' => fn (array $roots) => $roots['index'] . '/media',
'panel' => fn (array $roots) => $roots['kirby'] . '/panel',
'site' => fn (array $roots) => $roots['index'] . '/site',
'accounts' => fn (array $roots) => $roots['site'] . '/accounts',
'blueprints' => fn (array $roots) => $roots['site'] . '/blueprints',
'cache' => fn (array $roots) => $roots['site'] . '/cache',
'collections' => fn (array $roots) => $roots['site'] . '/collections',
'commands' => fn (array $roots) => $roots['site'] . '/commands',
'config' => fn (array $roots) => $roots['site'] . '/config',
'controllers' => fn (array $roots) => $roots['site'] . '/controllers',
'languages' => fn (array $roots) => $roots['site'] . '/languages',
'license' => fn (array $roots) => $roots['config'] . '/.license',
'logs' => fn (array $roots) => $roots['site'] . '/logs',
'models' => fn (array $roots) => $roots['site'] . '/models',
'plugins' => fn (array $roots) => $roots['site'] . '/plugins',
'sessions' => fn (array $roots) => $roots['site'] . '/sessions',
'snippets' => fn (array $roots) => $roots['site'] . '/snippets',
'templates' => fn (array $roots) => $roots['site'] . '/templates',
'roles' => fn (array $roots) => $roots['blueprints'] . '/users',
'i18n:rules' => fn (array $roots) => $roots['i18n'] . '/rules',

'index' => fn (array $roots) => static::$indexRoot ?? dirname(__DIR__, 3),
'assets' => fn (array $roots) => $roots['index'] . '/assets',
'content' => fn (array $roots) => $roots['index'] . '/content',
'media' => fn (array $roots) => $roots['index'] . '/media',
'panel' => fn (array $roots) => $roots['kirby'] . '/panel',
'site' => fn (array $roots) => $roots['index'] . '/site',
'accounts' => fn (array $roots) => $roots['site'] . '/accounts',
'blueprints' => fn (array $roots) => $roots['site'] . '/blueprints',
'cache' => fn (array $roots) => $roots['site'] . '/cache',
'collections' => fn (array $roots) => $roots['site'] . '/collections',
'commands' => fn (array $roots) => $roots['site'] . '/commands',
'config' => fn (array $roots) => $roots['site'] . '/config',
'controllers' => fn (array $roots) => $roots['site'] . '/controllers',
'languages' => fn (array $roots) => $roots['site'] . '/languages',
'license' => fn (array $roots) => $roots['config'] . '/.license',
'logs' => fn (array $roots) => $roots['site'] . '/logs',
'models' => fn (array $roots) => $roots['site'] . '/models',
'plugins' => fn (array $roots) => $roots['site'] . '/plugins',
'roles' => fn (array $roots) => $roots['blueprints'] . '/users',
'sessions' => fn (array $roots) => $roots['site'] . '/sessions',
'snippets' => fn (array $roots) => $roots['site'] . '/snippets',
'templates' => fn (array $roots) => $roots['site'] . '/templates',
'translations' => null,
];
}

Expand Down
41 changes: 40 additions & 1 deletion src/Cms/Language.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public function __construct(array $props)
$this->name = trim($props['name'] ?? $this->code);
$this->slugs = $props['slugs'] ?? [];
$this->smartypants = $props['smartypants'] ?? [];
$this->translations = $props['translations'] ?? [];
$this->translations = (new LanguageTranslations($this))->load($props['translations'] ?? []);
$this->url = $props['url'] ?? null;

if ($locale = $props['locale'] ?? null) {
Expand Down Expand Up @@ -409,6 +409,13 @@ public function save(): static
{
try {
$existingData = Data::read($this->root());

// inject translations from custom root
// returns existing translations
// if custom root is not defined as fallback
$existingData['translations'] = $this
->translationsObject()
->load($existingData['translations'] ?? []);
afbora marked this conversation as resolved.
Show resolved Hide resolved
} catch (Throwable) {
$existingData = [];
}
Expand All @@ -427,11 +434,30 @@ public function save(): static

ksort($data);

// save translations to the custom root and remove translations
// to prevent duplication write into the language file
if ($this->translationsObject()->root() !== null) {
$this->translationsObject()->save($data['translations'] ?? []);
$data['translations'] = [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it make sense to even unset it here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems setting empty array is correct.

With set empty array:

<?php

return [
    'code' => 'en',
    'default' => true,
    'direction' => 'ltr',
    'locale' => [
        'LC_ALL' => 'en_US'
    ],
    'name' => 'EN',
    'translations' => [

    ],
    'url' => NULL
];

With unseting:

<?php

return [
    'code' => 'en',
    'default' => true,
    'direction' => 'ltr',
    'locale' => [
        'LC_ALL' => 'en_US'
    ],
    'name' => 'EN',
    'url' => NULL
];

}

Data::write($this->root(), $data);

return $this;
}

/**
* Sets the translations data
*
* @return $this
*/
protected function setTranslations(array $translations = []): static
afbora marked this conversation as resolved.
Show resolved Hide resolved
{
$this->translations = (new LanguageTranslations($this))->load($translations);

return $this;
}

/**
* Private siblings collector
*/
Expand Down Expand Up @@ -475,12 +501,25 @@ public function toArray(): array

/**
* Returns the translation strings for this language
* @todo In v5, remove this method and
* rename `->translationsObject()` method with the `translations`.
* This will be a breaking change.
*/
public function translations(): array
{
return $this->translations;
}

/**
* Returns the language translations object for this language
* @todo In v5, rename this method name as `translations`
* @internal
*/
public function translationsObject(): LanguageTranslations
{
return new LanguageTranslations($this, $this->translations);
}

/**
* Returns the absolute Url for the language
*/
Expand Down
136 changes: 136 additions & 0 deletions src/Cms/LanguageTranslations.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<?php

namespace Kirby\Cms;

use Exception;
use Kirby\Data\Data;
use Kirby\Filesystem\F;

/**
* With helper methods provides get language translations or
* loads from custom `translations` root
* @since 4.2.0
*
* @package Kirby Cms
* @author Ahmet Bora <[email protected]>
* @link https://getkirby.com
* @copyright Bastian Allgeier
* @license https://getkirby.com/license
*/
class LanguageTranslations
{
protected array $data;

public function __construct(
protected Language $language,
self|array $translations = []
) {
$this->setTranslations($translations);
}

/**
* Returns a single translation string by key
*/
public function get(string $key, string $default = null): string|null
{
return $this->data[$key] ?? $default;
}

/**
* Loads the language translations based on custom roots for provided language code
*/
public function load(array $default = []): array
{
if ($file = $this->root()) {
try {
return Data::read($file);
} catch (Exception) {
return $default;
}
}

return $default;
}

/**
* Saves the language translations in the custom root
* @internal
*
* @return $this
*/
public function save(array $translations = []): static
{
$this->setTranslations($translations);

if ($root = $this->root()) {
Data::write($root, $translations);
}

return $this;
}

/**
* Returns custom translations root path if defined
*/
public function root(): string|null
{
$kirby = App::instance();
$root = $kirby->root('translations');
$file = ($root ?? '') . '/' . $this->language->code() . '.php';

if (
$root !== null &&
F::exists($file) === true
) {
return $file;
}

return null;
}

/**
* Removes a translation key
*
* @return $this
*/
public function remove(string $key): static
{
unset($this->data[$key]);
return $this;
}

/**
* Sets the translation key
*
* @return $this
*/
public function set(string $key, string|null $value = null): static
{
$this->data[$key] = $value;
return $this;
}

/**
* Set translations
*
* @return $this
*/
public function setTranslations(self|array $translations = []): static
{
$this->data = match (true) {
empty($translations) === true => $this->load(),
$translations instanceof self => $translations->toArray(),
default => $translations
};

afbora marked this conversation as resolved.
Show resolved Hide resolved
return $this;
}

/**
* Returns translations
*/
public function toArray(): array
{
return $this->data;
}
}
25 changes: 25 additions & 0 deletions tests/Cms/Languages/LanguageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,31 @@ public function testSave()
$this->assertSame('test', $data['custom']);
}

/**
* @covers ::save
*/
public function testSaveCustomTranslations()
{
new App([
'roots' => [
'index' => static::TMP,
'languages' => static::TMP . '/languages',
'translations' => static::TMP . '/translations'
]
]);

$file = static::TMP . '/translations/en.php';
F::write($file, '');
$language = new Language([
'code' => 'en'
]);
$language->save();
$language->variable('foo')->update('bar');
$translations = $language->translationsObject();

$this->assertSame(['foo' => 'bar'], $translations->toArray());
}

/**
* @covers ::toArray
* @covers ::__debugInfo
Expand Down
Loading
Loading