Drupal 7 multilingual site: each node URL is duplicated in all languages - drupal-7

I have a Drupal 7 multilingula site with 3 languages: english, arabic, and chinese.
It all work well, but I've just noticed that pages that are not translated have a duplicated URL for each language.
For example say I have www.example.com/node/12 (default language = English). This node doe snot have any translations.
However www.example.com/ar/node/12 and www.example.com/zh/node/12 both exist and point to the English page. They are even being index in Google.
How can I make these URL show a "Page not found" (which should be the expected behavior)?

There is an other way to translate your content, you can use the Entity Translation module. With this module, you can say that your content is not fully translatable, but just some fields on it (or all if all are translatable). With this system, you have just one node for each language, and this is a better things, if you navigate to your "ar/node/12" the node will be in arabic, if you navigate to the "zh/node/12", the node will be in chinese.
And generally this is better because there is some fields that doesn't need to be translated because it's the same in each language, like an entity reference, an address, etc.
Don't hesitate if you have other question.

Well, in the end I solved it with some custom code in hook_init:
$lang_name = $language->language ;
if ($lang_name == "ar" || $lang_name == "zh-hans") {
$has_translation = false;
if ($is_node) {
$translation_array = translation_node_get_translations($node->tnid);
$has_translation = isset($translation_array[$lang_name]);
}
if (!$has_translation) {
$path = drupal_get_path_alias(current_path());
$installed_languages = language_list();
$en = $installed_languages["en"];
drupal_goto($path, array('language' => $en), 301);
}
}
So, basically, on each page I check if we're on a language other than English (which means we're in a path starting with /ar or /zh). If that's the case and if we're on a node I check if there's an existing translation for that node and that language. If so we do nothing, but if not we redirect to the default path (without the language prefix).

Related

DNN Rewriting and cutting off Querystring even though I have a Regex setting

Our DNN website is rewriting our product SKU which is part of a Querystring when navigating from a Product Filter Page to a Detailed Product View page.
Unfortunately, some of our products have a forward slash in the SKU for example, BD0002/DSDS
The URL we are navigating to is https://dnndev.me/Product-View/sku/BD0002/DSDS, but DNN would cut off and rewrite the last part of the URL and would result in the following URL: https://dnndev.me/Product-View/sku/BD0002
I did try to add the following Regex code in the SEO settings section of DNN to ignore the re-writing of the page, but it does the same.
sku/(.*)/(.*)
I have also noticed that currently our website writes the sku without the = sign for the querystring. Currently it would be /sku/ and not ?sku= I discovered I can change this when I add |/sku| in the Keep in Querystring Regular Expression.
I have set the URL format to be Advanced in the web.config file. I don't want to change this to HumanFriendly as it breaks our module.
Our product filter page which contains the links to the Product View uses a mustache template with HttpUtility.UrlEncode for QueryStringSKU:
<a href='<%=DetailedPageRedirectLink%>/sku/{{QueryStringSKU}}'>More Info</a>
We then have a Detailed Product View module that listens for the QueryString. I did in the past try to use Encoding and Decoding, but DNN was doing its own thing and ignoring the Encoding and Decoding part so I wrote this crazy part of code that strips out part of the URL that is not part of the SKU.
string rawurlfromrequest = Request.RawUrl;
string checkifquerystringexist = Request.QueryString["sku"];
if(checkifquerystringexist != null)
{
var cleanSKU = rawurlfromrequest.Split(new[] { "sku/" }, StringSplitOptions.None)[1];
decodeprodCode = cleanSKU.Split(new[] { "&" }, StringSplitOptions.None)[0];
decodeprodCode = decodeprodCode.Split(new[] { "/search" }, StringSplitOptions.None)[0];
decodeprodCode = decodeprodCode.Split(new[] { "?fbclid=" }, StringSplitOptions.None)[0];
decodeSKU = HttpUtility.UrlDecode(decodeprodCode);
}
if (!string.IsNullOrWhiteSpace(decodeSKU) && IsEditable == false)
{
LoadProductDetails(decodeSKU);
}
So I would like to know, how can I only allow DNN to rewrite the first part of the URL and not the SKU part of the querystring when it contains a forward slash?
I found these links:
https://www.dnnsoftware.com/answers/disable-friendly-url-for-one-page
https://www.dnnsoftware.com/forums/threadid/542568/scope/posts/how-can-one-turn-off-friendly-urls-url-rewriting-etc-in-dnn-8
I had to escape the query string with uri.EscapedDataString() which will convert the / to %2F as mentioned by VDWWD.
I also discovered that some products contain a space in the SKU which made me decide to use EscapedDataString which will convert a space to %20.
I found this Table with the different Encoding methods on this post useful:
URL Encoding using C#
For some reason Request.Querystring['sku'] fetches the unencoded query string even though it is encoded in the URL. This is why I am using Request.RawUrl and stripping the query string from this.

How to get node path from id on multi lingual website?

I have a node, with nid. i.e. 78 and it's on French language (main language is English) so local name to it is:
fr/node/78
or just
node/78
?
Also that page has some alias, i.e. "hello" so it's relative path would be:
/fr/hello
How can I get that relative path?
Yes, I know that:
$alias = drupal_get_path_alias('node/78');
should give me my page alias, but it's not working, probably because of different language?!? Or that should be:
$alias = drupal_get_path_alias('fr/node/78');
I tried both, but none of them is working?!? I can't believe that there is no simple function available, which would take node id as parameter and return SEO (with alias) page path?!?
Please help. What is the easiest way to get node seo path on multi lingual website if I have nid?
Finally I got it. In case somebody else strugles with this:
// Returns root relative path
function get_node_url_ml ($nid, $language){
$defaultLanguage = language_default();
$local_path = 'node/' . $nid;
$url = drupal_lookup_path('alias', $local_path, $language);
if (!$url) $url = 'node/' . $nid;
if ($language != $defaultLanguage->language) $url = $language.'/'.$url;
return '/'.$url;
}

How to tag a particular version in jackrabbit / JCR?

I want to know if I can tag a particular version.
I want to use JCR in my project and we have many hierarchical type tree made of different type of nodes and we need to save a version after bunch of changes for staging or production. I want to know how can I tag the particular version so that all the changes made for particular node and its children will be saved.
Thanks!!!
Yes, you can tag the versions in a version history with strings of your choosing. But JCR 2.0 calls them "labels".
First, you need to find the VersionHistory for a particular node that's been checked in:
String path = ... // the path to the versioned node
VersionManager versionManager = session.getWorkspace().getVersionManager();
VersionHistory history = versionManager.getVersionHistory(path);
Then you can find the specific Version that you want to label, either by iterating over all the versions, by getting the "root version", or getting the base version. I won't show this since there's so many ways to do this.
// Find the version ...
Version versionToBeLabeled = ...
Then you can add a label to this version. Note that a label can only be used once within the version history of a single node, so when adding a label you can choose whether to move an existing label (if there is one) or throw an exception. Here's code that moves it if it already is used:
// Add a user-defined label to this version ...
String versionName = versionToBeLabeled.getName();
String versionLabel = "MyLabel";
boolean moveLabel = true;
VersionHistory.addVersionLabel(versionName, versionLabel, moveLabel);
Note that the version names are determined by the JCR implementation, whereas labels are user-defined. This is why it's often very convenient to add your own labels and then find particular Version instances by label rather than by some implementation-determined name:
Version foundVersion = versionHistory.getVersionByLabel(versionLabel);
And of course you can find out whether there's any Version in the history with a particular label:
if ( versionHistory.hasVersionLabel(versionLabel) ) {
// do something
}
or if a specific version has a label:
Version version = ...
if ( versionHistory.hasVersionLabel(version,versionLabel) {
// do something
}
and even remove a particular label:
versionHistory.removeLabel(versionLabel);
For more information, see Section 15.4 of the JCR 2.0 (JSR-283) specification.

drupal 7 file field formatter on custom node for javascript (to set up jQuizMe)

I have set up a module with custom node type (I called jquizme, after the javascript jQuizMe that I really like using). I set up two fields for the javascript files I need to supply to make it work (after the general jQuizMe-2.2.js file you need to add another two javascript files - one for settings and one for the quiz content).
Drupal saves the files as myjavascriptfile.js.txt - I tested them and they still work to make the jQuizMe interface - ok. the problem is, I want to add these files on the node page... the files will be different for each node. how can I access the files for the drupal_add_js() function so they will load the files for the node in question?
I tried setting up custom field formatters, but I don't know how to access the uri for the files of a given node automatically to put in the drupal_add_js() function (I can add a static file and it loads fine ... I did this with hook_node_view ( jquizme_node_view ).
So I just need a way to access the info for the files... how are they linked to each node? I can't find the connection.
As you probably noticed, I am a module writing newbie, and I probably won't understand much related to object oriented programming sorry, haven;t progressed to that level yet), but I am open to any answer. I am sure I left out important info, but this it already getting too long.
I also set up a special page earlier on to just see if I could get jQuizMe to work in Drupal so that is still in the code.
I have tried many answers (last six hours or so... too much to say here), the latest of which is using tokens, but that is not working. Here is what I have so far:
function jquizme_node_view($node, $view_mode, $langcode) {
switch ($node->type) {
case 'jquizme':
$items = field_get_items('node', $node, 'field_myfield', $node->language);
drupal_add_css(drupal_get_path('module', 'jquizme') . '/jQuizMe.css', >array('scope' => 'header'));
drupal_add_js(drupal_get_path('module', 'jquizme') . '/alert.js');
drupal_add_js(drupal_get_path('module', 'jquizme') . '/jQuizMe-2.2.js', >array('scope' => 'header'));
//drupal_add_js($tokens['node']['jquizme_js1_field'], array('scope' => >'header'));
//drupal_add_js($tokens['node']['jquizme_js2'], array('scope' => 'header'));
break;
}
}
Thanks in advance!
Can you try this?
// Let me assume that the field names of your two file fields
// are jquizme_js1_field and jquizme_js2_field for convenience..
function jquizme_node_view($node, $view_mode, $language) {
$items = field_get_items('node', $node, 'jquizme_js1_field');
_jquizme_add_js_from_filefield($items);
$items = field_get_items('node', $node, 'jquizme_js2_field');
_jquizme_add_js_from_filefield($items);
}
// Given the values of a filefield attached to some entity,
// adds them as JS files to the page.
function _jquizme_add_js_from_filefield($items = array()) {
foreach ($items as $item) {
$fid = &$item['fid'];
$file = file_load($fid);
if (!$file) {
continue; // Maybe the file got deleted..
}
$wrapper = file_stream_wrapper_get_instance_by_uri($file->uri);
$path = $wrapper->realpath();
// Ensure that the path exists and that it is a Javascript file..
if (file_exists($path) && preg_match('\.js$', $path)) {
drupal_add_js($path, array('type' => 'file'));
}
}
}

the best way to make codeigniter website multi-language. calling from lang arrays depends on lang session?

I'm researching hours and hours, but I could not find any clear, efficient way to make it :/
I have a codeigniter base website in English and I have to add a Polish language now. What is the best way to make my site in 2 language depending visitor selection?
is there any way to create array files for each language and call them in view files depends on Session from lang selection? I don't wanna use database.
Appreciate helps! I'm running out of deadline :/ thanks!!
Have you seen CodeIgniter's Language library?
The Language Class provides functions
to retrieve language files and lines
of text for purposes of internationalization.
In your CodeIgniter system folder you'll
find one called language containing sets
of language files. You can create your
own language files as needed in order
to display error and other messages in
other languages.
Language files are typically stored in
your system/language directory. Alternately
you can create a folder called language
inside your application folder and store
them there. CodeIgniter will look first
in your application/language directory.
If the directory does not exist or the
specified language is not located there
CI will instead look in your global
system/language folder.
In your case...
you need to create a polish_lang.php and english_lang.php inside application/language/polish
then create your keys inside that file (e.g. $lang['hello'] = "Witaj";
then load it in your controller like $this->lang->load('polish_lang', 'polish');
then fetch the line like $this->lang->line('hello'); Just store the return value of this function in a variable so you can use it in your view.
Repeat the steps for the english language and all other languages you need.
Also to add the language to the session, I would define some constants for each language, then make sure you have the session library autoloaded in config/autoload.php, or you load it whenever you need it. Add the users desired language to the session:
$this->session->set_userdata('language', ENGLISH);
Then you can grab it anytime like this:
$language = $this->session->userdata('language');
In the controller add following lines when you make the cunstructor
i.e, after
parent::Controller();
add below lines
$this->load->helper('lang_translate');
$this->lang->load('nl_site', 'nl'); // ('filename', 'directory')
create helper file lang_translate_helper.php with following function and put it in directory system\application\helpers
function label($label, $obj)
{
$return = $obj->lang->line($label);
if($return)
echo $return;
else
echo $label;
}
for each of the language, create a directory with language abbrevation like en, nl, fr, etc., under
system\application\languages
create language file in above (respective) directory which will contain $lang array holding pairs label=>language_value as given below
nl_site_lang.php
$lang['welcome'] = 'Welkom';
$lang['hello word'] = 'worde Witaj';
en_site_lang.php
$lang['welcome'] = 'Welcome';
$lang['hello word'] = 'Hello Word';
you can store multiple files for same language with differently as per the requirement
e.g, if you want separate language file for managing backend (administrator section) you can use it in controller as $this->lang->load('nl_admin', 'nl');
nl_admin_lang.php
$lang['welcome'] = 'Welkom';
$lang['hello word'] = 'worde Witaj';
and finally
to print the label in desired language, access labels as below in view
label('welcome', $this);
OR
label('hello word', $this);
note the space in hello & word you can use it like this way as well :)
whene there is no lable defined in the language file, it will simply print it what you passed to the function label.
I second Randell's answer.
However, one could always integrate a GeoIP such as http://www.maxmind.com/app/php
or http://www.ipinfodb.com/. Then you can save the results with the codeigniter session class.
If you want to use the ipinfodb.com api You can add the ip2locationlite.class.php file to your codeigniter application library folder and then create a model function to do whatever geoip logic you need for your application, such as:
function geolocate()
{
$ipinfodb = new ipinfodb;
$ipinfodb->setKey('API KEY');
//Get errors and locations
$locations = $ipinfodb->getGeoLocation($this->input->ip_address());
$errors = $ipinfodb->getError();
//Set geolocation cookie
if(empty($errors))
{
foreach ($locations as $field => $val):
if($field === 'CountryCode')
{
$place = $val;
}
endforeach;
}
return $place;
}
For easier use CI have updated this so you can just use
$this->load->helper('language');
and to translate text
lang('language line');
and if you want to warp it inside label then use optional parameter
lang('language line', 'element id');
This will output
// becomes <label for="form_item_id">language_key</label>
For good reading
http://ellislab.com/codeigniter/user-guide/helpers/language_helper.html
I've used Wiredesignz's MY_Language class with great success.
I've just published it on github, as I can't seem to find a trace of it anywhere.
https://github.com/meigwilym/CI_Language
My only changes are to rename the class to CI_Lang, in accordance with the new v2 changes.
When managing the actual files, things can get out of sync pretty easily unless you're really vigilant. So we've launched a (beta) free service called String which allows you to keep track of your language files easily, and collaborate with translators.
You can either import existing language files (in PHP array, PHP Define, ini, po or .strings formats) or create your own sections from scratch and add content directly through the system.
String is totally free so please check it out and tell us what you think.
It's actually built on Codeigniter too! Check out the beta at http://mygengo.com/string
Follow this https://github.com/EllisLab/CodeIgniter/wiki/CodeIgniter-2.1-internationalization-i18n
its simple and clear, also check out the document # http://ellislab.com/codeigniter/user-guide/libraries/language.html
its way simpler than
I am using such code in config.php:
$lang = 'ru'; // this language will be used if there is no any lang information from useragent (for example, from command line, wget, etc...
if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) $lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'],0,2);
$tmp_value = $_COOKIE['language'];
if (!empty($tmp_value)) $lang = $tmp_value;
switch ($lang)
{
case 'ru':
$config['language'] = 'russian';
setlocale(LC_ALL,'ru_RU.UTF-8');
break;
case 'uk':
$config['language'] = 'ukrainian';
setlocale(LC_ALL,'uk_UA.UTF-8');
break;
case 'foo':
$config['language'] = 'foo';
setlocale(LC_ALL,'foo_FOO.UTF-8');
break;
default:
$config['language'] = 'english';
setlocale(LC_ALL,'en_US.UTF-8');
break;
}
.... and then i'm using usualy internal mechanizm of CI
o, almost forget! in views i using buttons, which seting cookie 'language' with language, prefered by user.
So, first this code try to detect "preffered language" setted in user`s useragent (browser). Then code try to read cookie 'language'. And finaly - switch sets language for CI-application
you can make a function like this
function translateTo($language, $word) {
define('defaultLang','english');
if (isset($lang[$language][$word]) == FALSE)
return $lang[$language][$word];
else
return $lang[defaultLang][$word];
}
Friend, don't worry, if you have any application installed built in codeigniter and you wanna add some language pack just follow these steps:
1. Add language files in folder application/language/arabic (i add arabic lang in sma2 built in ci)
2. Go to the file named setting.php in application/modules/settings/views/setting.php. Here you find the array
<?php /*
$lang = array (
'english' => 'English',
'arabic' => 'Arabic', // i add this here
'spanish' => 'EspaƱol'
Now save and run the application. It's worked fine.

Resources