Get the current language code inside a TYPO3 Fluid Template - typo3

Is it possible to get the current language key (or code) in a TYPO3 Fluid template?
In the meantime I've found another solution using a view helper found here:
<?php
class Tx_AboUnitReservation_ViewHelpers_LanguageViewHelper extends Tx_Fluid_Core_ViewHelper_AbstractViewHelper
{
/**
* Get the current language
*/
protected function getLanguage()
{
if (TYPO3_MODE === 'FE') {
if (isset($GLOBALS['TSFE']->config['config']['language'])) {
return $GLOBALS['TSFE']->config['config']['language'];
}
} elseif (strlen($GLOBALS['BE_USER']->uc['lang']) > 0) {
return $GLOBALS['BE_USER']->uc['lang'];
}
return 'en'; //default
}
/**
* Return current language
* #return string
*/
public function render()
{
return $this->getLanguage();
}
}
Which I use in the fluid template as follows.
<f:alias map="{isGerman: 'de'}">
<f:if condition="{aboUnitReservation:language()} == {isGerman}">
<script type="text/javascript" src="{f:uri.resource(path:'js/jquery.ui.datepicker-de-CH.js')}"></script>
</f:if>
</f:alias>

Another solution using TypoScript object in Fluid template:
# German language
temp.language = TEXT
temp.language.value = at
# English language
[globalVar = GP:L = 1]
temp.language.value = en
[global]
lib.language < temp.language
And Fluid code:
<f:if condition="{f:cObject(typoscriptObjectPath: 'lib.language')} == 'at'">
<f:then>
...
</f:then>
<f:else>
...
</f:else>
</f:if>
Object temp.language can contain any values, of course.

You can just assign variable in your action:
$this->view->assign("sysLanguageUid", $GLOBALS['TSFE']->sys_language_uid);
and then read it in your view:
<f:if condition="{sysLanguageUid} == 0">
You're reading English version of page
</f:if>
on the other hand it would be easier and more comfortable to assign redy-to-use variable in controller as <f:if ...> block is quite simple and sometimes just uncomfortable:
switch ($GLOBALS['TSFE']->sys_language_uid) {
case 1:
$msg = "Bienvenidos";
break;
case 2:
$msg = "Willkommen";
break;
default:
$msg = "Welcome";
break;
}
$this->view->assign("myMessage", $msg);

In order to get the current langage, you can use the Page/LanguageViewHelper included with the VHS extension.
{v:page.language(languages: 'en,fr', pageUid: '0', normalWhenNoLanguage: 'en')}
Have a look here : http://fluidtypo3.org/viewhelpers/vhs/1.8.3/Page/LanguageViewHelper.html

my Solution is this:
data = TSFE:sys_language_uid
(The Output is the Language UID)
page = PAGE
page {
## Fluid-Template ##
10 = FLUIDTEMPLATE
10 {
## Variablen ##
variables {
pageTitle = TEXT
pageTitle.data = page:title
siteTitle = TEXT
siteTitle.data = TSFE:tmpl|setup|sitetitle
rootPage = TEXT
rootPage.data = leveluid:0
baseurl = TEXT
baseurl.value = {$config.domain}
pageLanguage = TEXT
pageLanguage.data = TSFE:sys_language_uid
}
## Settings ##
settings {
}
}
}
Now u can use the new "variables" in FLUID:
<f:if condition="{pageLanguage}==0">
<f:then>DE</f:then>
<f:else>EN</f:else>
</f:if>
Or you use this for more Lang-Code:
<f:if condition="{pageLanguage}==0">
<f:then>Do this</f:then>
<f:else if="{pageLanguage}==1">
Do this instead if variable two evals true
</f:else>
<f:else if="{pageLanguage}==2">
Or do this if variable three evals true
</f:else>
<f:else>
Or do this if nothing above is true
</f:else>
</f:if>

Another approach via the TYPO3 languageAspect
Example within an Extbase Extension (e.g. showAction)
# instantiate TYPO3 Context and get language aspect
$languageAspect = GeneralUtility::makeInstance(Context::class)->getAspect('language');
# now assign any property of the actual language to the view. In this example: the language uid.
$this->view->assign('actualLanguageId', $languageAspect->getId());
Example tested in TYPO3 10.4 LTS

Another option could be to use the v:page.languageMenu ViewHelper from the VHS extension. It would allow you to combine with other ViewHelpers and use something like the following in a Fluid template:
{namespace v=Tx_Vhs_ViewHelpers}
<v:page.languageMenu as="languages">
<!-- gets the current language key -->
{languages -> v:iterator.filter(propertyName: 'current', filter: 1)}
<!-- iterates over flag names of all languages which apply to the current page -->
<f:for each="{languages -> v:iterator.extract(key: 'flag')}" as="languageFlagName">
<!-- example suggestion: image -->
<f:image src="{f:uri.resources(path: 'Images/Flags/{languageFlagName}.png')}" />
<!-- example suggestion: label read from LLL:EXT:myext/Resources/Private/Language/locallang.xml:languages.$languageFlagName -->
<f:translate key="languages.{languageFlagName}" default="{languageFlagName} />
</f:for>
</v:page.languageMenu>
There is a lot more you can do with the values returned from each of these ViewHelpers - you can for example use v:var.set to define new variables in the template which contain extracted flag names:
<!-- Using {languages} inside <v:page.languageMenu> like above -->
{languages -> v:iterator.filter(propertyName: 'current', filter: 1) -> v:var.set(name: 'currentLanguage')}
<!-- variable {currentLanguage} now contains an array of values describing the current language -->

In TYPO3 10 and above you could access directly the language uid and use it together with a condition in your Fluid-Template (see here some hints for using Fluid):
<f:if condition="{data.sys_language_uid} = 1">
<f:then>
This is language with uid "1"
</f:then>
<f:else>
This is language with other uid than "1"
</f:else>
</f:if>

Related

How to output COA_INT in fluid template form element

I'd like to pass an extension GP variable to a form that I built outside of the plugin. When trying to do so, I used a COA_INT which turned out to output <INT_SCRIPT. instead of the value.
In order to do so, I built this typoscript object:
lib.gpSWord = COA_INT
lib.gpSWord {
# Protects from XSS!
stdWrap.htmlSpecialChars = 1
10 = TEXT
10 {
stdWrap.data = GP:tx_indexedsearch_pi2|search|sword
}
}
and then tried to output it like this (as suggested here:
<f:form.textfield name="search[sword]" value="<f:format.raw>{f:cObject(typoscriptObjectPath: 'lib.gpSWord')}</f:format.raw>" id="tx-indexedsearch-searchbox-sword" class="tx-indexedsearch-searchbox-sword font-h2" />
And also tried building up the variable before hand using f:variable but nothing worked.
The expected result would be that the variable would be set and the value would then be passed to the form.textfield viewhelper but appearently this does'nt work either.
You should commit the variable in Typoscript (setup) with:
page.10 = FLUIDTEMPLATE
page.10.variables.gpSWord < lib.gpSWord
Then you are able to use it in you Fluidtemplate with:
<f:format.raw>{gpSWord}</f:format.raw>
lib.gpSWord = COA_INT
lib.gpSWord {
stdWrap {
wrap = &tx_indexedsearch_pi2[sword]=|
data = GP:tx_indexedsearch_pi2|sword
if.isTrue.data = GP:tx_indexedsearch_pi2|sword
}
}
Try this for GP.

How to remove strings at left of "-" in filename in TYPO3 Fluid?

Using TYPO3 8 LTS, we got many standardized filenames like:
ABC_105-Report.pdf
DEFGH_110-Brochure.ppt
We need to remove whatever is at left of "-" so it becomes a list like this in TYPO3 Frontend:
Report.pdf
Brochure.ppt
We are already using VHS Viewhelpers which contains Format:Eliminiate, Substring so it may be part of the solution.
One possible solution is VHS: Format / PregReplaceViewHelper.
<f:alias map="{filenames: {
0: 'ABC_105-Report.pdf',
1: 'DEFGH_110-Brochure.ppt',
2: 'FilenameWithoutMagicChar.jpg',
3: 'Multiple-Magic-Chars.jpg'}}">
<ul>
<f:for each="{filenames}" as="filename">
<li>
{v:format.pregReplace(
subject: filename,
pattern: '/^[^-]*-/',
replacement: ''
)}
</li>
</f:for>
</ul>
</f:alias>
Result:
Report.pdf
Brochure.ppt
FilenameWithoutMagicChar.jpg
Magic-Chars.jpg
If 'Chars.jpg' is required instead of 'Magic-Chars.jpg', the regular expression is /-.*/.
a very basic typoscript viewhelper:
in fluid:
<f:cObject typoscriptObjectPath="lib.filenameStub" data="{filename}" />
in typoscript:
lib.filenameStub = TEXT
lib.filenameStub {
current = 1
split {
max = 2
token = -
returnKey = 1
}
}

TYPO3 - Is it not allowed to use names in typoscript?

I modified a part of a working typoscript template from:
10 = COA
10.stdWrap.wrap = <div class="toggleBar">|</div>
10 {
...
to
toggleBarDIV = COA
toggleBarDIV.stdWrap.wrap = <div class="toggleBar">|</div>
toggleBarDIV {
...
Now it is not working anymore. Is it not allowed to use names in typoscript?
Where is it allowed and where not? Where can I get more infos?
As far as I know, COA keys must always be numbers (10,20,1,2...);
There is a feature request about using more meaningful keys:
https://forge.typo3.org/issues/45488

Replace line break

In Fluid Template and tx_news, I need to replace line breaks with "\n" for passing into JavaScript function.
If a JavaScript string contains line break, console will print "Unexpected token."
<a onclick="doSomething('{newsItem.bodytext}');">Click me</a>
How can you replace line breaks with "\n" in this example?
AS urbantrout already wrote: you can write an own viewhelper in PHP.
But you also can use a TypoScript-Viewhelper:
<a onclick="doSomething('{newsItem.bodytext -> f:cObject(typoscriptObjectPath: \'lib.nlReplace\')}');">Click me</a>
(as you are in a string you need to escape the inner ')
and some TypoScript like
lib.nlReplace = TEXT
lib.nlReplace {
current = 1
stdWrap.replacement {
1 {
search = #\n#
replace = \\n
useRegExp = 1
}
}
}
You could write your own ViewHelper and use it like this:
{namespace ns=Vendor\ExtensionName\ViewHelpers}
<a onclick="doSomething('{newsItem.bodytext -> ns:viewhelperName()}');">Click me</a>
More infos here: Developing a custom ViewHelper

TYPO3 Fluid complex if conditions

I am trying to write the following if condition in fluid but it is not working as I would hope.
Condition
As part of a for loop I want to check if the item is the first one or 4th, 8th etc
I would have thought the following would work but it display the code for every iteration.
<f:if condition="{logoIterator.isFirst} || {logoIterator.cycle % 4} == 0">
I have managed to get it working with a nested if but it just feels wrong having the same section of code twice and also having the cycle check use a <f:else> instead of == 0
<f:if condition="{logoIterator.isFirst}">
<f:then>
Do Something
</f:then>
<f:else>
<f:if condition="{logoIterator.cycle} % 4">
<f:else>
Do Something
</f:else>
</f:if>
</f:else>
</f:if>
TYPO3 v8
Updated the answer for TYPO3 v8. This is quoted from Claus answer below:
Updating this information with current situation:
On TYPO3v8 and later, the following syntax is supported which fits
perfectly with your use case:
<f:if condition="{logoIterator.isFirst}">
<f:then>First</f:then>
<f:else if="{logoIterator.cycle % 4}">n4th</f:else>
<f:else if="{logoIterator.cycle % 8}">n8th</f:else>
<f:else>Not first, not n4th, not n8th - fallback/normal</f:else>
</f:if>
In addition there is support for syntax like this:
<f:if condition="{logoIterator.isFirst} || {logoIterator.cycle} % 4">
Is first or n4th
</f:if>
Which can be more appropriate for some cases (in particular when using
a condition in inline syntax where you can't expand to tag mode in
order to gain access to the f:else with the new if argument).
TYPO3 6.2 LTS and 7 LTS
For more complex if-Conditions (like several or/and combinations) you can add your own ViewHelper in your_extension/Classes/ViewHelpers/. You just have to extend Fluids AbstractConditionViewHelper. The simple if-ViewHelper that shipps with Fluid looks like this:
class IfViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionViewHelper {
/**
* renders <f:then> child if $condition is true, otherwise renders <f:else> child.
*
* #param boolean $condition View helper condition
* #return string the rendered string
* #api
*/
public function render($condition) {
if ($condition) {
return $this->renderThenChild();
} else {
return $this->renderElseChild();
}
}
}
All you have to do in your own ViewHelper is to add more parameter than $condition, like $or, $and, $not etc. Then you just write your if-Conditions in php and render either the then or else child. For your Example, you can go with something like this:
class ExtendedIfViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionViewHelper {
/**
* renders <f:then> child if $condition or $or is true, otherwise renders <f:else> child.
*
* #param boolean $condition View helper condition
* #param boolean $or View helper condition
* #return string the rendered string
*/
public function render($condition, $or) {
if ($condition || $or) {
return $this->renderThenChild();
} else {
return $this->renderElseChild();
}
}
}
The File would be in your_extension/Classes/ViewHelpers/ExtendedIfViewHelper.php Then you have to add your namespace in the Fluid-Template like this (which enables all your self-written ViewHelpers from your_extension/Classes/ViewHelpers/ in the template:
{namespace vh=Vendor\YourExtension\ViewHelpers}
and call it in your template like this:
<vh:extendedIf condition="{logoIterator.isFirst}" or="{logoIterator.cycle} % 4">
<f:then>Do something</f:then>
<f:else>Do something else</f:else>
</vh:extendedIf>
Edit: updated.
Updating this information with current situation:
On TYPO3v8 and later, the following syntax is supported which fits perfectly with your use case:
<f:if condition="{logoIterator.isFirst}">
<f:then>First</f:then>
<f:else if="{logoIterator.cycle % 4}">n4th</f:else>
<f:else if="{logoIterator.cycle % 8}">n8th</f:else>
<f:else>Not first, not n4th, not n8th - fallback/normal</f:else>
</f:if>
In addition there is support for syntax like this:
<f:if condition="{logoIterator.isFirst} || {logoIterator.cycle} % 4">
Is first or n4th
</f:if>
Which can be more appropriate for some cases (in particular when using a condition in inline syntax where you can't expand to tag mode in order to gain access to the f:else with the new if argument).
v:if.condition will be deprecated in vhs V2.0
use v:if stack instead:
https://github.com/FluidTYPO3/vhs/issues/493
You could also use the If Condition Extend ViewHelper provided by the VHS extension:
<v:if.condition>
<v:if.condition.extend>
{logoIterator.isFirst} || {logoIterator.cycle % 4} == 0
</v:if.condition.extend>
<f:then>Output if TRUE</f:then>
<f:else>Output if FALSE</f:else>
</v:if.condition>
On a side note: the VHS extension provides lots of useful ViewHelpers. I feel a lot of them should be included in TYPO3 Fluid.
For many cases its enough to use an array-comparison - so you don't have to create a custom view-helper.
AND
<f:if condition="{0:user.number,1:user.zip}=={0:123,1:01234}">
OR
<f:if condition="{0:user.number,1:user.zip}!={0:false,1:false}">
Sadly this works just to check if a variable is set and not against a value. But for many cases this is enough.
PS:(with this array comparison you can also compare strings)
In addition to Daniels' answer, I made a ViewHelper that accepts multiple conditions, with either an "and"-mode (default) or an "or"-mode:
<?php
namespace TLID\Contentelements\ViewHelpers;
class IfViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper {
/**
* Checks conditions
*
* #param mixed $checks
* #param string $type
*
* #return boolean whether is array or not
*/
public function render($checks, $type = "and") {
$success = $type === "and" ? true : false;
$doc = new \DOMDocument();
$doc->loadHTML($this->renderChildren());
$xpath = new \DOMXpath($doc);
// get store values
$storeNodes = $xpath->query("//body/store");
$store = "";
foreach ($storeNodes as $storeNode) {
foreach ($storeNode->childNodes as $childNode) {
$store .= $doc->saveHTML($childNode);
}
}
// do the actual check
foreach ($checks as $check) {
if (
($type === "and" && (is_array($check) && count($check) === 0 || is_object($check) && get_object_vars($check) === 0 || empty($check))) ||
(is_array($check) && count($check) !== 0 || is_object($check) && get_object_vars($check) !== 0 || !empty($check))
) {
$success = $type === 'and' ? false : true;
break;
}
}
// render content
$renderQueryElement = $success ? "success" : "failure";
$renderNodes = $xpath->query("//body/" . $renderQueryElement);
$content = "";
foreach ($renderNodes as $renderNode) {
foreach ($renderNode->childNodes as $childNode) {
$content .= $doc->saveHTML($childNode);
}
}
//insert contents
$matches;
$content = preg_replace("/<use[^>]*><\/use>/", $store, $content);
//return rendered content
return $content;
}
}
?>
Though it can be written alot better, it works.
Here is how i use it:
{namespace vhs=TLID\contentelements\ViewHelpers}
<vhs:if checks="{0: settings.link}">
<f:comment><!-- store the content --></f:comment>
<store>
<f:if condition="{images}">
<f:for each="{images}" as="image">
<f:image image="{image}" alt="{image.description}" title="{image.title}" />
</f:for>
</f:if>
<vhs:if checks="{0: settings.headline, 1: settings.text}" type="or">
<success>
<div>
<f:if condition="{settings.headline}"><h2><f:format.nl2br><vhs:shy>{settings.headline}</vhs:shy></f:format.nl2br></h2></f:if>
<f:if condition="{settings.text}"><p><f:format.nl2br><vhs:shy>{settings.text}</vhs:shy></f:format.nl2br></p></f:if>
</div>
</success>
</vhs:if>
</store>
<f:comment><!-- use the content of this container on success --></f:comment>
<success>
<vhs:link href="{settings.link}" target="{settings.target}" class="box">
<use />
</vhs:link>
</success>
<f:comment><!-- use the content of this container on failure --></f:comment>
<failure>
<div class="box">
<use />
</div>
</failure>
</vhs:if>
It additionally has a store-element, because i don't like it to write the same code twice. So you can optionally save some fluid and pass it to both the success and failure containers without the need for repetition.
It is possible to implement complex if conditions with a combination of f:if, v:variable.set and v:math. Use the math ViewHelper to do the magic and store its result in a variable. Then use if comparators to validate and act upon it.
Here is my sample code:
<f:for each="{customers}" as="customer" iteration="iterator">
<v:variable.set name="isFirst" value="{v:math.modulo(a: iterator.cycle, b: settings.itemsperrow, fail: 0)}" />
<f:if condition="{isFirst}==1">
<div class="row">
</f:if>
<div class="col-md-{settings.colWidth}">
<div class="clientlogo_ref">
<f:image src="{customer.logo.originalResource.publicUrl}" />
</div>
</div>
<f:if condition="{isFirst}==0">
</div>
</f:if>
</f:for>
This code begins / ends a grid row for every X item, defined by settings.itemsperrow. This is variable and can be set in the plugin's configuration. It uses modulo to calculate iterator.cycle (counter beginning with 1) mod settings.itemsperrow. If the result is 1, it is the first element of a row. 0 means it is the last, so row must be closed.
Yes it feels wrong but this is the only way you can do it. This is a very good site for viewhelper :: https://fluidtypo3.org/viewhelpers/fluid/master/IfViewHelper.html
For me best way to use 'f:cycle'. If i need devide for rows each 3th elements i just do:
<v:variable.set name="wraper" value='</div><div class="row">' />
<f:for each="{items}" as="item" iteration="itemIterator">
....
<f:cycle values="{0: '', 1: '', 2: '{wraper}'}" as="cycle">
{cycle -> f:format.raw()}
</f:cycle>
...
</f:for>
If you have CObjects help this workaround for a Logical OR:
# Sidebar | 1 ColPos = 78
lib.sidebar1 < styles.content.get
lib.sidebar1.select.where = colPos=78
# Sidebar | 2 ColPos = 79
lib.sidebar2 < styles.content.get
lib.sidebar2.select.where = colPos=79
#LogicalOR
lib.tempLogicalOrSidebar = COA
lib.tempLogicalOrSidebar {
10 < lib.sidebar1
10.stdWrap.override.cObject =< lib.sidebar2
}
FLUID IF CONDITION:
<f:if condition="{f:cObject(typoscriptObjectPath: 'lib.tempLogicalOrSidebar.10')}">
Status 2017:
Here is an example of a modern VHS viewhelper condition using the stack attribute. This example also contains a NULL check and a logical or (||) to show how it is done.
<v:if stack="{0: '{itemId}', 1:'==', 2:NULL, 3: '||', 4: '{itemId}', 5: '==', 6: '{falMedia.uid}'}">
<f:then>
...
</f:then>
</v:if>
Mind that NULL is NOT quoted!