Skip to content

Commit

Permalink
Merge pull request #78 from sitegeist/feature/translatableComponents
Browse files Browse the repository at this point in the history
[FEATURE] Labels data structure that allows translations per component
  • Loading branch information
s2b committed Feb 8, 2021
2 parents 041b1f9 + 87140ba commit de2f971
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 3 deletions.
139 changes: 139 additions & 0 deletions Classes/Domain/Model/Labels.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php

namespace SMS\FluidComponents\Domain\Model;

use SMS\FluidComponents\Interfaces\ComponentAware;
use SMS\FluidComponents\Interfaces\ConstructibleFromArray;
use SMS\FluidComponents\Interfaces\ConstructibleFromNull;
use SMS\FluidComponents\Utility\ComponentLoader;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;

class Labels implements ComponentAware, \ArrayAccess, ConstructibleFromArray, ConstructibleFromNull
{
/**
* Static label values that should override those defined in language files
*
* @var array
*/
protected $overrideLabels = [];

/**
* Cache for component labels file
*
* @var string
*/
protected $labelsFile;

/**
* Constructor
*
* @param array $overrideLabels
*/
public function __construct(array $overrideLabels = [])
{
$this->overrideLabels = $overrideLabels;
}

/**
* Generate object based on an array passed to the component
*
* @param array $overrideLabels
* @return self
*/
public static function fromArray(array $overrideLabels): self
{
return new self($overrideLabels);
}

/**
* Generate object, even if component parameter is optional and omitted
*
* @return self
*/
public static function fromNull(): self
{
return new self;
}

/**
* Receive component context to determine language file path
*
* @param string $componentNamespace
* @return void
*/
public function setComponentNamespace(string $componentNamespace): void
{
$this->componentNamespace = $componentNamespace;
}

/**
* Check if language label is defined
*
* @param mixed $identifier
* @return boolean
*/
public function offsetExists($identifier): bool
{
return $this->offsetGet($identifier) !== null;
}

/**
* Return value of language label
*
* @param mixed $identifier
* @return string|null
*/
public function offsetGet($identifier): ?string
{
return $this->overrideLabels[$identifier]
?? LocalizationUtility::translate($this->generateLabelIdentifier($identifier));
}

/**
* Set an override language label
*
* @param mixed $identifier
* @param mixed $value
* @return void
*/
public function offsetSet($identifier, $value): void
{
$this->overrideLabels[$identifier] = $value;
}

/**
* Remove an override language label
*
* @param mixed $identifier
* @return void
*/
public function offsetUnset($identifier): void
{
unset($this->overrideLabels[$identifier]);
}

/**
* @param string $identifier
* @return string
*/
protected function generateLabelIdentifier(string $identifier): string
{
if (!$this->labelsFile) {
$this->labelsFile = $this->generateLabelFilePath();
}
return sprintf('LLL:%s:%s', $this->labelsFile, $identifier);
}

/**
* @return string
*/
protected function generateLabelFilePath(): string
{
$componentLoader = GeneralUtility::makeInstance(ComponentLoader::class);
$componentFile = $componentLoader->findComponent($this->componentNamespace);
$componentName = basename($componentFile, '.html');
$componentPath = dirname($componentFile);
return $componentPath . DIRECTORY_SEPARATOR . $componentName . '.labels.xlf';
}
}
51 changes: 50 additions & 1 deletion Documentation/DataStructures.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,55 @@ no matter which image variant is used:
In addition, the different implementations offer additional properties that can
be used safely after checking the `type` property accordingly.

## Translations

`SMS\FluidComponents\Domain\Model\Labels` (alias: `Labels`)

Each component can define its own set of translation labels. Those labels can be accessed via an
instance of the `Labels` class that can needs to be defined as an optional component parameter:

Fluid Component `Molecule/TeaserCard/TeaserCard.html`:

```xml
<fc:component>
<fc:param name="labels" type="Labels" optional="1" />
<fc:renderer>
[...]
<a href="...">{labels.readMore}</a>
</fc:renderer>
</fc:component>
```

The `readMore` language label can be defined in an XLIFF translation file that is placed next to
the component html file. Its file name must be *{ComponentName}.labels.xlf* (or *de.{ComponentName}.labels.xlf*
for German translations), so in this example the component will pick up the following language file:

Language file `Molecule/TeaserCard/TeaserCard.labels.xlf`:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.0" xmlns:t3="http://typo3.org/schemas/xliff">
<file source-language="en" datatype="plaintext" original="messages" date="2021-02-02T18:55:32Z" product-name="my_extension">
<header/>
<body>
<trans-unit id="readMore">
<source>Read more</source>
</trans-unit>
</body>
</file>
</xliff>
```

All labels can also be overwritten when the component is called:

```xml
<my:molecule.teaserCard labels="{readMore: 'Overwritten read more'}" />
```

Please note that due to the way those labels are accessed, it is not possible to use `.` in the
label identifier. We recommend to use lower camel case (`readMore`) or snake case (`read_more`) in
component language files.

## Navigations

`SMS\FluidComponents\Domain\Model\Navigation` (alias: `Navigation`)
Expand Down Expand Up @@ -324,7 +373,7 @@ default:

## Type Aliases

The included data structures can also be defined with their alias. These are `Image`, `Link`, `Typolink`, `Navigation` and `NavigationItem`.
The included data structures can also be defined with their alias. These are `Image`, `Link`, `Typolink`, `Labels`, `Navigation` and `NavigationItem`.

```xml
<fc:component>
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,12 @@ defined interface instead of debating implementation details.
Feature References

* [ViewHelper Reference](Documentation/ViewHelperReference.md)
* [Included Data Structures](Documentation/DataStructures.md)
* [Data Structures](Documentation/DataStructures.md)
* [Links and Typolink](Documentation/DataStructures.md#links-and-typolink)
* [Images](Documentation/DataStructures.md#images)
* [Translations](Documentation/DataStructures.md#translations)
* [Navigations](Documentation/DataStructures.md#navigations)
* [DateTime](Documentation/DataStructures.md#datetime)
* [Component Prefixers](Documentation/ComponentPrefixers.md)
* [Component Settings](Documentation/ComponentSettings.md)

Expand Down
2 changes: 1 addition & 1 deletion ext_emconf.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
'state' => 'stable',
'uploadfolder' => false,
'clearCacheOnLoad' => false,
'version' => '2.4.1',
'version' => '2.5.0',
'constraints' => [
'depends' => [
'typo3' => '9.5.0-11.9.99',
Expand Down
3 changes: 3 additions & 0 deletions ext_localconf.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
}
$GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']['fc'][] = 'SMS\\FluidComponents\\ViewHelpers';

// Register type aliases
if (!isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['typeAliases'])) {
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['typeAliases'] = [];
}
Expand All @@ -19,6 +20,8 @@
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['typeAliases']['Typolink'] = \SMS\FluidComponents\Domain\Model\Typolink::class;
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['typeAliases']['Navigation'] = \SMS\FluidComponents\Domain\Model\Navigation::class;
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['typeAliases']['NavigationItem'] = \SMS\FluidComponents\Domain\Model\NavigationItem::class;
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['fluid_components']['typeAliases']['Labels'] = \SMS\FluidComponents\Domain\Model\Labels::class;

if (!isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['fluidComponents.partialsInComponents'])) {
$GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['fluidComponents.partialsInComponents'] = false;
}
Expand Down

0 comments on commit de2f971

Please sign in to comment.