Jump to content

Change Language Inherit for empty multi-lang fields


Orkun
 Share

Recommended Posts

Is it possible to write a hook where I could change the language inherit for empty multi lang fields not to take the default? Like for example:

All Doctors(with template doctors) are taking the italian(or some other language) field value when the german(is not defined as default language) field value is empty. Otherwise it should take the german field value. 

Perhaps a pagefield(single select) with all languages where I can choose the new "pseudo default inherit". Would this be possible or. do you have a idea how to accomplish this?

Lang Inherit.png

Greetings

Orkun

Link to comment
Share on other sites

You have to hook LanguagesPageFieldValue::getStringValue, adding your own logic there and retrieving the values using $event->object->getLanguageValue($language). You could set the default language to use in a page field (template=language) added to the user template.

  • Like 2
Link to comment
Share on other sites

1 hour ago, BitPoet said:

You have to hook LanguagesPageFieldValue::getStringValue, adding your own logic there and retrieving the values using $event->object->getLanguageValue($language). You could set the default language to use in a page field (template=language) added to the user template.

Hi @BitPoet

Could you possibly explain that further? I have to confess, that I don't really understand the whole Multilang Structure/Process of Processwire. 

What I have so far is only this:

// Written inside an autoload module called sitehelper

$this->addHookAfter('LanguagesPageFieldValue::getStringValue', $this, 'hookLangInherit');

public function hookLangInherit(HookEvent $event){
	
}

I tried to split the process in two points:

1. The Hook should only be executed on multilang fields of the template "doctors".

2. It should replace the default lang with the new language which you have chosen in "absender" pages as new inheritvalue.

- (Absender Pages are linked to doctor pages via pagefield - multi selection - It should take the languagevalue/inheritvalue always from the first absender which is linked to the doctor)

 

Does $event->object->getLanguageValue($language) return the current page the hook is executed or what?

I apologize @BitPoet if I am pushy but I just want to understand the cause and learn from it, since I don't know all rough edges of ProcessWire. Would be glad If you could spend some of your time.

Greetings Orkun

Link to comment
Share on other sites

In PW's multi-language handling for text-based fields, these fields don't hold a regular string (as they do in non-ml setups) but a LanguagesPageFieldValue object. This object stores the field values (strings) for the different languages. When you retrieve the string value of such a field, PHP's standard magic calls _toString on the object. The implementation for _toString is done in the hookable getStringValue method.

Unfortunately, while the LanguagesPageFieldValue objects do know their page and field, the information isn't accessible from a hook. But you can always access $this->wire('page') to get the current page in the frontend. Then you can retrieve the absender through $page->absender->first().

Some quick&dirty code for the function that probably illustrates this better (the above sounds more complicated than it really is):

/* ... */
	$mlObj = $event->object; // LanguagesPageFieldValue object
	$currPage = $this->wire('page');
	if($currPage->template == "doctors") {
		$absender = $currPage->absender->first();
		$absLang = $absender->sprache;
		$event->return = $mlObj->getLanguageValue($sprache);
	}
/* ... */

As long as you apply use this logic to all language fields on a template, this approach should work fine. Otherwise, it would probably get complicated.

  • Like 2
Link to comment
Share on other sites

  • 3 weeks later...
On 8.11.2016 at 7:01 PM, BitPoet said:

In PW's multi-language handling for text-based fields, these fields don't hold a regular string (as they do in non-ml setups) but a LanguagesPageFieldValue object. This object stores the field values (strings) for the different languages. When you retrieve the string value of such a field, PHP's standard magic calls _toString on the object. The implementation for _toString is done in the hookable getStringValue method.

Unfortunately, while the LanguagesPageFieldValue objects do know their page and field, the information isn't accessible from a hook. But you can always access $this->wire('page') to get the current page in the frontend. Then you can retrieve the absender through $page->absender->first().

Some quick&dirty code for the function that probably illustrates this better (the above sounds more complicated than it really is):


/* ... */
	$mlObj = $event->object; // LanguagesPageFieldValue object
	$currPage = $this->wire('page');
	if($currPage->template == "doctors") {
		$absender = $currPage->absender->first();
		$absLang = $absender->sprache;
		$event->return = $mlObj->getLanguageValue($sprache);
	}
/* ... */

As long as you apply use this logic to all language fields on a template, this approach should work fine. Otherwise, it would probably get complicated.

Thanks @BitPoet

This works when I access a doctor(template/page) directly. What when I am fetching the doctor output and putting it out on a overview-page where you can access the details of a doctor per url segment?

Link to comment
Share on other sites

This doesn't work so well somehow. Now I am doing the hook when the $currPage Template is "overview-content-page or fetch-content-page". This both templates are used to output News/Babies/Events/Specialities/Doctors per Url Segments etc... It Inherits the new language but It looks like, that it always outputs content in the new inherited language even when the field has content in other languages than the inherited language (Should that supposed to be like that?). My current Code Snippet looks like this:

public function hookLangInherit(HookEvent $event){
	$mlObj = $event->object; // LanguagesPageFieldValue object
	$currPage = $this->wire('page');

	if($currPage->is("template=overview-content-page|fetch-content-page")) {
	    //check if detail page is accessed
	    if($this->input->urlSegment1){

                // The overview-content and fetch-content pages are living under a specific rootParent which has assigned a specific "absender"
                $absender = $currPage->rootParent->choose_sender_2016;

                // Get The new language by a custom pagefield(single) from the "absender"
                $sprache = $absender->inheritLanguages;

                $newLangValue = $mlObj->getLanguageValue($sprache);
                if($newLangValue == ""){
                    // Fallback to default language when value is empty in the inherited language
                    $newLangValue = $mlObj->getLanguageValue(1010);
                }

                // Get the new values in that language
                $event->return = $newLangValue;
	    }
	}
}

EDIT: I added a little fallback to the code snippet above. When the Language Value is empty in the new inherited language it uses the default language again as new inherited language.

Greetings Orkun

Link to comment
Share on other sites

Anything new on that topic @BitPoet?

I really need this since it is one of the top priorities of my customer. I understand why he wants this, since it is irritating when you go on a page and there is no content for the actual language. The default fallback at the moment is french, but some pages doesn't have french content and the customer doesn't want to fillout the "Default/Fallback Language Tab" since it is a "step more" he needs to take care of. I thought this through and I am thinking of going another way to avoid "empty frontend pages". How about filling the default language tab with the content of the language that you have specified under the "absender". I think this would work good for text and textarea fields. The disadvantages of this approach is, that I need to take care of the hooking code since fields and templates could be changed or new ones could be added. The hooking code could also be bloated, since there are many templates and fields. In the end it means just more maintenance then the first approach, but more control over what I am doing. How do you think of it guys?

Link to comment
Share on other sites

One thing I'd definitely never do is mix values of different languages in one language's field. It's bound to come back and haunt you.

To get around template additions/changes, you could simply add a (hidden) field to them. Then, in your hook, simply check $page->template->hasField("yourhiddenfield") instead of comparing template names.

Lastly, I'd change your code above a bit to always try the user's language first, then try to fall back to the absender's language and lastly the default.

public function hookLangInherit(HookEvent $event){
	$mlObj = $event->object; // LanguagesPageFieldValue object
	$currPage = $this->wire('page');

	if($currPage->template->hasField("flag_absendersprache")) {
	    //check if detail page is accessed
	    if($this->input->urlSegment1){

                // The overview-content and fetch-content pages are living under a specific rootParent which has assigned a specific "absender"
                $absender = $currPage->rootParent->choose_sender_2016;
                // Get The new language by a custom pagefield(single) from the "absender"
                $sprache = $absender->inheritLanguages;

                $usprache = $this->user->language;
                $dsprache = $this->languages->getDefault();

                // Take the first non-empty value in order of precedence:
                // First the current user's, then the absender's, then the default language
                foreach(array($usrpache, $sprache, $dsprache) as $lang) {
                    $newLangValue = $mlObj->getLanguageValue($lang);
                    if($newLangValue != "") break;
                }

                // Get the new values in that language
                $event->return = $newLangValue;
	    }
	}
}

 

  • Like 2
Link to comment
Share on other sites

  • 2 weeks later...

Thanks for your help @BitPoet

I am using your code now and it works still halfaway. Somehow navbar, footer, header and stuff like that are also translated to the new lang inherit even when they have content in the actual active language. They only should take the new language inherit when they have no content in it. Somehow the check inside the loop has no effect I think? Or is it something else? 

Link to comment
Share on other sites

16 hours ago, BitPoet said:

Definitely an issue with the loop (a typo, to be precise). This:


               foreach(array($usrpache, $sprache, $dsprache) as $lang) {

should contain $usprache instead of $usrpache.

 

lol, how I could miss that... haha :lol:

Thanks, now It's working perfectly (Y)

Link to comment
Share on other sites

  • 1 month later...
On 30.1.2017 at 0:30 PM, Nukro said:

Do I need to make another Hook for this or alter the existing one? How would I make this?

Take a look here: https://processwire.com/api/ref/pagefile/description/ and here: https://github.com/processwire/processwire/blob/35df716082b779de0e53a3fcf7996403c49c9f8a/wire/core/Pagefile.php#L300-L310

Link to comment
Share on other sites

1 minute ago, BitPoet said:

I just whipped up a little test on a freshly installed PW, and there it definitely works without being logged in, so it has to be something else. Is the module/include adding the hook loaded for non-superusers?

Nevermind. It had to do with the field 'flagLangInherit'. I had set access only for superuser for this specific field to see and had forgot it to switch it back when the site was live...

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...