Welcome Guest, Not a member yet? Register   Sign In
language subdomains
#1

I've been helping to rebuild a largish site in CodeIgniter 3 for some time now. Our plan all along has been to specify the language to be displayed as a subdomain. E.g., spanish will be es.example.com and italian would be http it.example.com etc.  We accomplished this by extending the CI_Controller class and adding a $language class var. In our constructor, we sniff out the value for the current language from the domain:

Code:
 // Set site display language from sub-domain only.
 $this->language = self::get_site_display_language_preference();
where the get_site_display_language_preference() function looks like this:
Code:
/**
* @return string valid language code
*
*/
public static function get_site_display_language_preference()
{
// Extract language code from sub-domain.
$domain_parts = explode(".", $_SERVER['HTTP_HOST']);
$possible_subdomain_language = array_shift($domain_parts);

if (self::is_allowed_subdomain_language($possible_subdomain_language)) {
return $possible_subdomain_language;
} else {
return config_item('language'); // default
}

} // get_site_display_language_preference()

This seems to work pretty well. In any controller, we can happily load the appropriate language file like so:
Code:
$this->lang->load('menus/main_menu', $this->language);
And this command knows to look in the right location for the file.

The problem we have is with redirects. I created these two methods in a test controller:
Code:
/**
* Quick test to see how redirection works with language issues
*/
public function redir() {
redirect("/test/arrival");
}

public function arrival() {
die("this is arrival, lang=" . $this->language);
}
If I visit es.example.com/test/redir in my browser, this redirect drops the language portion of my domain and redirects to example.com/test/arrival. The desired behavior would be for it to preserve the "es" in the original url and redirect to es.example.com/test/arrival. This problem appears to happen because the redirect function makes use of the site_url function which looks like this:
Code:
if ( ! function_exists('site_url'))
{
/**
* Site URL
*
* Create a local URL based on your basepath. Segments can be passed via the
* first parameter either as a string or an array.
*
* @param string $uri
* @param string $protocol
* @return string
*/
function site_url($uri = '', $protocol = NULL)
{
return get_instance()->config->site_url($uri, $protocol);
}
}
This apparently just prepends whatever value you have for base_url in application/config/config.php.

What do you guys recommend in this situation? We can put this type of code in the config.php file but I wonder if it's very safe or whatever.:
Code:
$config['base_url'] = 'https://example.com/';

// base_url needs to switch based on whether there is a language subdomain in use
$domain_parts = explode(".", $_SERVER['HTTP_HOST']);
$subdomain = array_shift($domain_parts);
$allowed_subdomains = array('es', 'it', 'de');
if (in_array($subdomain, $allowed_subdomains)) {
$config['base_url'] = 'https://' . $subdomain . '.example.com/';
}

Is there any recommend best practice for using subdomains?
Reply
#2

Have you considered extending the url_helper class:

https://www.codeigniter.com/user_guide/g...lpers.html

And replace the redirect to accommodate your subdomain redirects?
Reply
#3

(11-20-2017, 05:53 AM)Kaosweaver Wrote: Have you considered extending the url_helper class:

https://www.codeigniter.com/user_guide/g...lpers.html

And replace the redirect to accommodate your subdomain redirects?

There's no such thing as an "url_helper class". It's not a class, just a bunch of global functions declared by the same php file.
To "extend" them means to completely replace them.
Reply
#4

(11-20-2017, 06:31 AM)Narf Wrote:
(11-20-2017, 05:53 AM)Kaosweaver Wrote: Have you considered extending the url_helper class:

https://www.codeigniter.com/user_guide/g...lpers.html

And replace the redirect to accommodate your subdomain redirects?

There's no such thing as an "url_helper class". It's not a class, just a bunch of global functions declared by the same php file.
To "extend" them means to completely replace them.

Sigh... my bad, the link covered it with this note:
The term “extend” is used loosely since Helper functions are procedural and discrete and cannot be extended in the traditional programmatic sense. Under the hood, this gives you the ability to add to or or to replace the functions a Helper provides.
I assumed the OP can read.


to the OP each function in the helper is wrapped with:

PHP Code:
if ( ! function_exists('redirect')) 

creating the MY_url_helper.php file in the application/helpers folder with the single re-written redirect function to handle the subdomain redirect would work.
Reply
#5

The documentation does contain a section on "extending" helper functions. Thank you, Narf, for clarifying that we are talking about completely replacing them. I'm still interested in this possibility but would like to clarify the implications.

Our project already has a application/helpers/MY_url_helper.php file but this doesn't currently replace any native CI functions. Looking at the CI redirect() function, I'm wondering if it might make sense to replace instead the site_url function:
Code:
function redirect($uri = '', $method = 'auto', $code = NULL)
{
if ( ! preg_match('#^(\w+:)?//#i', $uri))
{
$uri = site_url($uri);
}

// IIS environment likely? Use 'refresh' for better compatibility
if ($method === 'auto' && isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== FALSE)
{
$method = 'refresh';
}
elseif ($method !== 'refresh' && (empty($code) OR ! is_numeric($code)))
{
if (isset($_SERVER['SERVER_PROTOCOL'], $_SERVER['REQUEST_METHOD']) && $_SERVER['SERVER_PROTOCOL'] === 'HTTP/1.1')
{
$code = ($_SERVER['REQUEST_METHOD'] !== 'GET')
? 303 // reference: http://en.wikipedia.org/wiki/Post/Redirect/Get
: 307;
}
else
{
$code = 302;
}
}

switch ($method)
{
case 'refresh':
header('Refresh:0;url='.$uri);
break;
default:
header('Location: '.$uri, TRUE, $code);
break;
}
exit;
}

I thought this function just retrieved base_url from my config.php, but looking at CI_Config::site_url() it looks like this function is also fairly complicated. Perhaps I should extend CI_Config instead and override site_url() in the MY_Config?

I'm especially concerned about a couple of things:
1) Remember that I will be examining the current value of $_SERVER['HTTP_HOST'] to try and extract a two-letter language string to ascertain which language the user expects. I think I can do this safely. We will then prepend this two-char language string to the base_url from the config file -- ideally nothing at all would be different about the MY_Config::site_url function.
2) The site_url helper function and CI_Config::site_url functions appear to be used in about a dozen places in the system directory alone. My project also uses it 15-20 times. I wonder if it's risky to change this function.
Reply
#6

I'm really hoping someone knowledgeable might chime in on whether or not its advisable to modify the site_url function.

It looks like site_url() appears in the the core CI files in 16 places:
Code:
$ schphp "site_url\s*(" system
system/core/Config.php:    public function site_url($uri = '', $protocol = NULL)
system/core/Config.php:     * @used-by    CI_Config::site_url()
system/libraries/Calendar.php:            $this->next_prev_url = $this->CI->config->site_url($this->CI->router->class.'/'.$this->CI->router->method);
system/libraries/Javascript/Jquery.php:        $controller = (strpos('://', $controller) === FALSE) ? $controller : $this->CI->config->site_url($controller);
system/helpers/html_helper.php:                    $img .= ' src="'.get_instance()->config->site_url($v).'"';
system/helpers/html_helper.php:                        $link .= 'href="'.$CI->config->site_url($v).'" ';
system/helpers/html_helper.php:                $link .= 'href="'.$CI->config->site_url($href).'" ';
system/helpers/form_helper.php:            $action = $CI->config->site_url($CI->uri->uri_string());
system/helpers/form_helper.php:            $action = $CI->config->site_url($action);
system/helpers/url_helper.php:    function site_url($uri = '', $protocol = NULL)
system/helpers/url_helper.php:        return get_instance()->config->site_url($uri, $protocol);
system/helpers/url_helper.php:        return $CI->config->site_url($CI->uri->uri_string());
system/helpers/url_helper.php:            ? site_url($uri)
system/helpers/url_helper.php:            : (preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri));
system/helpers/url_helper.php:        $site_url = preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri);
system/helpers/url_helper.php:            $uri = site_url($uri);
Reply
#7

Those first two in the Config class itself don't matter. One's just a comment, the other is the form definition.

Calendar.php (which we don't appear to be using) just makes links, so it should be fine.

Javascript/Jquery.php (which we also don't appear to be using) -- it's not clear whether this will be affected or not. We don't appear to be using this either.

html_helper.php -- We also don't appear to be using this. Looking at the source, I don't think this would be broken either *as long as* our images and javascript and so on will still be accessible under their language domain. E.g., if some image like www.example.com/images/my-image.jpg can also be accessed at es.example.com/images/my-image.jpg then we should be fine. **There may be SEO considerations** because we might have multiple URLs for the same resource.

form_helper.php -- the site_url function is only used in here to set an action attribute for a form -- this probably should work as expected. If you're viewing en.example.com, you want the form to post to en.example.com.

url_helper.php -- not 100% certain, but the actions in here like redirect, current_url, anchor, and anchor_popup all seem like they should work fine too.
Reply
#8

I don't understand why you're not replacing the "replace" function.

Right after:

PHP Code:
if ( ! preg_match('#^(\w+:)?//#i'$uri))
{
$uri site_url($uri);


you just do this:

PHP Code:
// base_url needs to switch based on whether there is a language subdomain in use
$domain_parts explode("."$_SERVER['HTTP_HOST']);
$subdomain array_shift($domain_parts);
$allowed_subdomains = array('es''it''de');
if (
in_array($subdomain$allowed_subdomains)) {
 
   // do your replacement URL stuff here


Leave everything else the same.
Reply
#9

(11-18-2017, 12:42 PM)sneakyimp Wrote: I've been helping to rebuild a largish site in CodeIgniter 3 for some time now. Our plan all along has been to specify the language to be displayed as a subdomain. E.g., spanish will be es.example.com and italian would be http it.example.com etc.  We accomplished this by extending the CI_Controller class and adding a $language class var. In our constructor, we sniff out the value for the current language from the domain:

Code:
 // Set site display language from sub-domain only.
 $this->language = self::get_site_display_language_preference();
where the get_site_display_language_preference() function looks like this:
Code:
/**
* @return string valid language code
*
*/
public static function get_site_display_language_preference()
{
// Extract language code from sub-domain.
$domain_parts = explode(".", $_SERVER['HTTP_HOST']);
$possible_subdomain_language = array_shift($domain_parts);

if (self::is_allowed_subdomain_language($possible_subdomain_language)) {
return $possible_subdomain_language;
} else {
return config_item('language'); // default
}

} // get_site_display_language_preference()

This seems to work pretty well. In any controller, we can happily load the appropriate language file like so:
Code:
$this->lang->load('menus/main_menu', $this->language);
And this command knows to look in the right location for the file.

The problem we have is with redirects. I created these two methods in a test controller:
Code:
/**
* Quick test to see how redirection works with language issues
*/
public function redir() {
redirect("/test/arrival");
}

public function arrival() {
die("this is arrival, lang=" . $this->language);
}
If I visit es.example.com/test/redir in my browser, this redirect drops the language portion of my domain and redirects to example.com/test/arrival. The desired behavior would be for it to preserve the "es" in the original url and redirect to es.example.com/test/arrival. This problem appears to happen because the redirect function makes use of the site_url function which looks like this:
Code:
if ( ! function_exists('site_url'))
{
/**
* Site URL
*
* Create a local URL based on your basepath. Segments can be passed via the
* first parameter either as a string or an array.
*
* @param string $uri
* @param string $protocol
* @return string
*/
function site_url($uri = '', $protocol = NULL)
{
return get_instance()->config->site_url($uri, $protocol);
}
}
This apparently just prepends whatever value you have for base_url in application/config/config.php.

What do you guys recommend in this situation? We can put this type of code in the config.php file but I wonder if it's very safe or whatever.:
Code:
$config['base_url'] = 'https://example.com/';

// base_url needs to switch based on whether there is a language subdomain in use
$domain_parts = explode(".", $_SERVER['HTTP_HOST']);
$subdomain = array_shift($domain_parts);
$allowed_subdomains = array('es', 'it', 'de');
if (in_array($subdomain, $allowed_subdomains)) {
$config['base_url'] = 'https://' . $subdomain . '.example.com/';
}

Is there any recommend best practice for using subdomains?
English only
Reply




Theme © iAndrew 2016 - Forum software by © MyBB