Jump to content

LanguageLocalizedURL


mcmorry

Recommended Posts

The PageLinkAbstractor is also a 3rd party module, and not one that I would try to build around at all. If something doesn't work with that module, then I would just say the two are not compatible with each other. I don't think many people are actually using that PageLinkAbstractor module anyway. I don't personally use it. If I've been developing a site in a subdirectory and then launch it to the root on another server, I just do a simple search+replace on the DB dump file, using a text editor (TextWrangler/BBEdit) replacing "/subdirectory/" with "/". However, this is something that I do plan to add core support for. The plan is that FieldtypeTextarea will encode local links to IDs like {123} when saving, and then unencode them back to URLs at runtime. We can collaborate on making everything work together when the time comes.

I think I understand the problem you mentioned with a link created in TinyMCE being a static link in the language of the user making the edit. Would it be possible to just disable the URL parsing when the user is inserting a link?

if(wire('process') == 'ProcessPageEditLink') {
  // don't parse URLs
} else {
  // do parse URLs
}

The point of the above is so that the URL is always stored in the default language. Then hook into FieldtypeTextarea::formatValue and have it hunt down and replace them with the current language specific version, at runtime. I'm not so familiar with how the language localized URL module works, so this is based on guesses, but it might work something like this:

public function init() {
 $this->addHookAfter('FieldtypeTextarea::formatValue', $this, 'hookFormatValue'); 
}

public function hookFormatValue(HookEvent $event) {

 $language = wire('user')->language; 

 // no need to continue if user's language is default
 if($language->isDefault()) return; 

 $page = $event->arguments[0]; // not using, but here if you need it
 $field = $event->arguments[1]; // not using, but here if you need it
 $value = $event->arguments[2];
 $rootUrl = rtrim(wire('config')->urls->root, '/'); 

 // find links
 if(!preg_match_all('{href=[\'"]([-_./a-z0-9]+)}[\'"]', $value, $matches)) return;

 foreach($matches[1] as $key => $url) {

   // slice off subdirectory if site is running from one, so we have a clean URL from PW-install root
   if($rootUrl && strpos($url, $rootUrl) === 0) $url = substr($url, strlen($rootUrl)); 

   // see if this is a link to a PW-managed page in the site
   $linkPage = wire('pages')->get($url);
   if(!$linkPage->id) continue; 

   // now that you have the $linkPage and know the $language you should be able to
   // get the language URL right? I don't know how you do it, so I'll pretend: 
   $langUrl = $this->getLanguageUrl($linkPage, $language); 

   // replace the default language URL with the language URL
   $value = str_replace($matches[0][$key], "href='$langUrl'", $value); 
 } 

 $event->return = $value; 
}
Link to comment
Share on other sites

Thanks Ryan for the suggestion, I also thought about something like this, but it doesn't work as simple as this, turning off while process is "ProcessPageEditLink". The problem is the page tree select in the link edit dialog which is implement through various jsscripts/php gates...

The solution I found works well, and has the nice effect that it shows the page titles in the page list select in the language of the textfield you're making the link. Though requires some modules in the core to be changed.

Link to comment
Share on other sites

Using

if( strpos( $_SERVER['HTTP_REFERER'], "page/link/?id" ) !== false ) return;

I got it to ignore multilang url when in edit link modal.

The hook for the FieldtypeTextarea::formatValue doesn't work at all. If I look at the link abstractor (which also doesn't work) and try the same to hook before FieltypeTextarea::wakeupValue it hooks in but fails completely with any code I try to parse the $value. Sometimes the $value is an array or object or something and even converting it makes no effect. The preg match or any string test fails. Any variant with hook after or other trying FieldtypeText fails. Does this have to do with it being FieltypeTextareaLanguage?

Any ideas what's going on or how it could be done? I'm just running in circles.

Edit: finally I got it somehow working. It's hooking before the FieldtypeTextareaLanguage wakeupValue, somehow it required completely different handling of the value because it's language field, I'm not really sure if all is correct but it works in admin and frontend. After textarea with links is saved, it shows modified urls but stored in DB is still the "real" page url. Following the code:

public function init() {
 $this->addHookBefore('FieldtypeTextareaLanguage::wakeupValue', $this, 'hookWakeupValue');
 $this->addHookAfter('Page::path', $this, 'mlUrl');
}

...

public function hookWakeupValue(HookEvent $event) {

$page = $event->arguments[0]; // not using, but here if you need it
$field = $event->arguments[1]; // not using, but here if you need it
$value = $event->arguments[2];

if(empty($value)) return;

$language = wire('user')->language;

if(!is_object($value)) return;
if($value instanceof LanguagesPageFieldValue) {
	// get the language value for parsing for page urls
	$replaced_value = $value->getLanguageValue($language->id);
}

$rootUrl = rtrim(wire('config')->urls->root, '/');

// find all links
if(!preg_match_all('{href=[\'"]([-_./a-z0-9]+)[\'"]}', $replaced_value, $matches)) return;

foreach($matches[1] as $key => $url) {

	// slice off subdirectory if site is running from one, so we have a clean URL from PW-install root
	if($rootUrl && strpos($url, $rootUrl) === 0) $url = substr($url, strlen($rootUrl));

	// see if this is a link to a PW-managed page in the site
	$linkPage = wire('pages')->get($url);

	// if not go on to the next
	if(!$linkPage->id) continue;

	// get language url
	$langUrl = $linkPage->url;

	// replace the default language URL with the language URL
	$replaced_value = str_replace($matches[0][$key], "href='$langUrl'", $replaced_value);
}

// set the modified language value
$value = $value->setLanguageValue($language->id, $replaced_value);
$event->return = $value;
}
Link to comment
Share on other sites

Thanks Soma for your updates. I merged your code on master. This is really an important improvement :)

I've seen that you already started to code the install procedure to create the template for proxy pages, but is commented out. Does it need more testing?

You are doing a grate job. I would like to have more time to help you, but currently I'm focused on too many projects.

Thanks again.

Link to comment
Share on other sites

Thx mcmorry.

Yes the install/deinstall I started working on is commented out, as it need more testing and refining.

I'm currently running into issue with the new hook on textarealanguage when I go to edit a textarea language field in admin. Not sure what's going on but will try to fix it. Will make a pull request when I got it fixed.

Link to comment
Share on other sites

Sent a new pull request today.

Fixed the new url replacing feature in textarealanguage and improved behavior.

Now if you insert a link in tinymce, it replaces urls found with the correct localized url for each language value when saving page. I changed the hook to the sleepValue() so it get's saved to db and we're done. No need to add this overhead on frontend too this way.

Also added a check to prevent execution on field edit screen. When editing a language textarea with tinymce as inputfield, there were some strange error I have no idea where and why. It was finding a href="./templates/" and showed a selector error although there's no such link... I don't think it need to be executed there anyway so this should be fine.

  • Like 1
Link to comment
Share on other sites

  • 3 weeks later...

Hello everyone, I've been following this thread a bit and I've implemented the module, configured it as the manual instructs and it seems to work very well. However, there is a question: what's the right procedure to build a language switcher for the front end. I've tried:

<a href='./?language=default'>Português</a> |
<a href='./?language=en'>English</a> |
<a href='./?language=fr'>Français</a>

However, even though the language do switches, the URL remains the same. For example:

  1. Current page is domain.com/pt/contactos/
  2. Clicking the link to french, gets me to the page domain.com/pt/contactos/?language=fr while what I want was for it to go to domain.com/fr/contacts/

I am sorry if the solution has already been published in the forum, but I just can't find it.

Thanks in advance for any kind of help.

Link to comment
Share on other sites

The module has no url param functions so I'm a little surprized it changes language, so this may from PW itself or you did something to get the url param? However you can simply do something like this:

Somewhere in the template I have this before the language switch

$lang = $user->language;
$langname = $lang->name == 'default' ? 'en' : $lang->name;

Then the language switch links

// get users current language
$lang_temp = $user->language;

// set language to default
$user->language = $languages->get("default");
$st = $langname == 'en' ? " class='on'" : '';
echo $page->language_published->has( $pages->get("/en/") ) ? "<a$st href='{$page->url}'>EN</a>" : '';

// set language to german
$user->language = $languages->get("de");
$st = $langname == 'de' ? " class='on'" : '';
echo $page->language_published->has( $pages->get("/de/") ) ? "<a$st href='{$page->url}'>DE</a>" : '';

// set users language back
$user->language = $lang_temp;

Like this, the "$page->url" will return the language url already, if the users language is switched while creating the links for each language. So no need for urls params, cookies, sessions or anything.

The page field "language_published" references the proxy language pages, so we can easily check if the language is really published (checked), which means it holds the proxy page. Since it's a page reference field we can simply use $page->language_published->has() method.

Edit: added missing script needed before.

  • Like 1
Link to comment
Share on other sites

Something I noticed when using this module. When I use the $page->render() method for the proxy template. I have a auto hook on Page::render that add in some script before the body end with php string replace. For some reason it's getting executed 2 times resulting in duplicated code. I'd guess the page rendered with ->render() in a template causes the system to render it twice?

Edit:

This is used in the proxy template


$page = $modules->get('LanguageLocalizedURL')->parseUrl();
echo $page->render();

And using this hook to str_replace html

$this->addHookAfter('Page::render', $this, 'addScripts');

So I have to add this to check if already there

// if already there (in case of using $page->render())
if(strpos($event->return,$script) !== false) return;
Link to comment
Share on other sites

That makes sense as to why Page::render is getting called twice, and your hook is getting called twice. It seems to all be the proper behavior. So like you found, you just need to avoid adding your script twice. I think the way you are doing it is good. Though here's another alternative that might be slightly faster:

public function addScripts(HookEvent $event) {
 static $completed = false; 
 if($completed) return;
 // ... code to add stuff to the rendered output
 $completed = true; 
}
  • Like 1
Link to comment
Share on other sites

OK. I think I get it ... but just for understanding:

I got two domains (example.de, example.com). I want example.de to show the german content and example.com to show the english content. How do my pagetree and the PageTitleLangage-fields have to look like?

Link to comment
Share on other sites

The simplest way is to have the domain.com point to domain.de. Done. Now it depends on how you create the language switch. Just adding the domain in front of the url would be enough.

But you will end up having domain.de/de/.. and domain.com/en/...

The module has an option to hide default language folder, so /de/ will not show. But for alternative languages there currently no way, without adding anther option to hide them all.

Let me know if that would for out for you, and I'll try to implement it.

Link to comment
Share on other sites

Well it looks kind of stupid if I'd have domains like domain.de/de/ and domain.com/en/ (... but this is how it is right now (sgym.eu/en/ and sgym.de/de/)...). So, yes, it would be great to have a option like "use multiple domains", which hides /de/ and /en/.

P.S.: I used Oliver's old language translation module for this page originally.

Link to comment
Share on other sites

Ok, I have implemented a language domains option you can now enable in the module settings. I did some testing on my local install and so far everything works as it should. I just commited the update and it's ready to use. urlSegments , pager, page links in language text fields still work and I didn't notice any problems. However it's not sure any other side-effects pop up I may missed, apart from the ones already there.

From readme:

This module now supports language domains. So you can have each language mapped to its own domain. To make this work you need to have the different domains point to the default domain and PW install. Then enable this option in the module settings and enter your domains in the textarea, each domain on its own line.

The format is [domain:langcode] i.e.:

domain.com:en
domain.de:de
domain.fr:fr

You could also use subdomains:

en.domain.com:en
de.domain.com:de
fr.domain.com:fr

Result:

Now you can access the different languages simply through the different domains. For example the "/about/" page would be available like this:

domain.com/about/
domain.de/ueber/
domain.fr/au-sujet-de/
  • Like 6
Link to comment
Share on other sites

  • 1 month later...

Awesome module, it’s working very well. One small note for the documentation, it currently says the following:

‘Create a page field i.e. language_published. Set the template to the one you use for the proxy pages. Set inputfield type to checkboxes.’

Being a bit of a novice user, it took me some time to figure out that with page field you mean a field with type ‘page’. So maybe even more clear would be if you’d rewrite it to the following:

‘Create a field i.e. language_published of type ‘Page’. Under Input > Selectable Pages, set the template to the one you use for the proxy pages. Set input field type to checkboxes.’

  • Like 1
Link to comment
Share on other sites

Hi, thank you for your suggestion.

I had some difficulties to find the documentation that you reported, because you was speaking about the "inline" documentation. Instead I was searching on online documentation where the steps are described in details:

https://github.com/mcmorry/PW-language-localized-URL#readme

Anyway I've fixed it as you suggested. It will be easier for all users to setup the modue.

Thank you

  • Like 1
Link to comment
Share on other sites

Slightly off-topic but would there be a way to hide the language proxy pages from the document tree (for a client)? Since these pages have no content and children. This topic is about a similar question, although I did not find an aswer there. It would be great if these could be treated the same way as the 404 page (visibility wise), which also only shows itself to superusers in the cms.

— there’s always the css quickfix though, but that would feel a bit sloppy working with such a great system and all

Edited by thijs
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
×
×
  • Create New...