Skip to content

Commit

Permalink
support subdirectory
Browse files Browse the repository at this point in the history
  • Loading branch information
jemikanegara committed Nov 5, 2024
1 parent 711bf15 commit 1f469d7
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 23 deletions.
2 changes: 1 addition & 1 deletion browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ if (isBrowser()) {

function initTranslation() {
// replace links with lang (for SEO purposes)
if (translationMode != 'subdomain' && shouldReplaceLinks && paramsLang && (paramsLang != originalLanguage)) {
if (translationMode != 'subdomain' && translationMode != 'subdirectory' && shouldReplaceLinks && paramsLang && (paramsLang != originalLanguage)) {
replaceLinks(window, {langParam, lang: paramsLang, translationMode: options.translationMode});
}
getTranslations(window, apiKey, options)
Expand Down
94 changes: 85 additions & 9 deletions extractOptionsFromScript.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const detectRobot = require("./utils/detectRobot");
const getPrefixedPathname = require("./utils/translation-mode/getPrefixedPathname");
const getUnprefixedPathname = require("./utils/translation-mode/getUnprefixedPathname");

function replaceCustomLinks(window, customLinks = {}) {
function replaceCustomLinks(window, customLinks = {}, domainFromServer) {
// find all tags with href and src
const tagsWithSrc = window.document.querySelectorAll("[src]");
const tagsWithHref = window.document.querySelectorAll("[href]");
Expand All @@ -13,13 +13,28 @@ function replaceCustomLinks(window, customLinks = {}) {
const src = tag.getAttribute("src");
if (customLinks[src]) {
tag.setAttribute("src", customLinks[src]);
} else {
const url = new URL(src, window.location.origin);
const hostname = url.hostname;
const hostnameWithoutWWW = hostname.replace("www.", "");
if (domainFromServer == hostnameWithoutWWW) {
tag.setAttribute("src", url.href);
}
}
}

for (let tag of tagsWithHref) {
const href = tag.getAttribute("href");
if (customLinks[href]) {
tag.setAttribute("href", customLinks[href]);
} else {
const url = new URL(href, window.location.origin);
const hostname = url.hostname;
const hostnameWithoutWWW = hostname.replace("www.", "");
console.log("DOMAIN", domainFromServer, hostnameWithoutWWW)
if (domainFromServer == hostnameWithoutWWW) {
tag.setAttribute("href", url.href);
}
}
}
}
Expand All @@ -30,13 +45,15 @@ function replaceCustomLinks(window, customLinks = {}) {
* @param {any} window - The global window object.
* @param {Object} optsArgs - Options for extraction.
* @param {string|null} optsArgs.activeLanguage - The active language (optional).
* @param {string|null} optsArgs.domainFromServer - The domain from the server (optional).
* @param {"globalseo"|"weploy"} [optsArgs.brand] - The brand name.
* - "searchParams": Extract options from query parameters.
* - "subdomain": Extract options from subdomains.
* - "pathname": Extract options from the path.
*/
function extractOptionsFromScript(window, optsArgs = {
activeLanguage: null,
domainFromServer: null,
}) {
if (isBrowser()) {
if (!window.translationCache) {
Expand Down Expand Up @@ -76,6 +93,7 @@ function extractOptionsFromScript(window, optsArgs = {
const DATA_CUSTOM_LINKS = "data-custom-links"

const DATA_TRANSLATION_CACHE = "data-translation-cache";
const DATA_SOURCE_ORIGIN = "data-source-origin";

// WORKER ATTRIBUTES
// const DATA_PREVENT_INIT_TRANSLATION = "data-prevent-init-translation" // default: false
Expand All @@ -84,13 +102,23 @@ function extractOptionsFromScript(window, optsArgs = {
const DATA_ACTIVE_SUBDOMAIN = "data-active-subdomain"; // default: undefined
const activeSubdomain = window.translationScriptTag.getAttribute(DATA_ACTIVE_SUBDOMAIN);

const DATA_ACTIVE_SUBDIRECTORY = "data-active-subdirectory"; // default: undefined
const activeSubdirectory = window.translationScriptTag.getAttribute(DATA_ACTIVE_SUBDIRECTORY);

// prevent initial translation for subdomain (but allow translation on dynamic content)
if (activeSubdomain) {
window.preventInitialTranslation = true;
window.activeSubdomain = activeSubdomain;
}

const customLinksAttribute = window.translationScriptTag.getAttribute(`${DATA_CUSTOM_LINKS}-${activeSubdomain}`);
if (activeSubdirectory) {
window.preventInitialTranslation = true;
window.activeSubdirectory = activeSubdirectory;
}

const activeServerSideLang = activeSubdomain || activeSubdirectory;

const customLinksAttribute = window.translationScriptTag.getAttribute(`${DATA_CUSTOM_LINKS}-${activeServerSideLang}`);
let customLinks = {};
try {
// format: [oldUrl,newUrl], [oldUrl,newUrl]
Expand All @@ -100,7 +128,7 @@ function extractOptionsFromScript(window, optsArgs = {
return acc;
}, {});

replaceCustomLinks(window, customLinks);
replaceCustomLinks(window, customLinks, optsArgs.domainFromServer);
} catch (e) {
customLinks = {};
}
Expand Down Expand Up @@ -153,8 +181,18 @@ function extractOptionsFromScript(window, optsArgs = {
const allowedLangs = (allowedLangAttr || "").trim().toLowerCase().split(",").filter(lang => lang && lang.trim() != originalLang).map(lang => lang.trim());

// always use original language for subdomain
const activeLang = translationMode == "subdomain" && !window.isWorker ? window.globalseoActiveLang : (window.globalseoActiveLang || paramsLang || originalLang);
let activeLang = (window.globalseoActiveLang || paramsLang || originalLang);

if (translationMode == "subdomain" && !window.isWorker) {
activeLang = window.globalseoActiveLang
}

if (translationMode == "subdirectory" && !window.isWorker) {
activeLang = window.globalseoActiveLang
}

console.log("DOC LANG", activeLang)

if (activeLang && (window.document.documentElement.lang != activeLang)) {
window.document.documentElement.lang = activeLang;
}
Expand All @@ -165,7 +203,16 @@ function extractOptionsFromScript(window, optsArgs = {

function handleLinkTags() {
const domainWithoutWww = window.location.hostname.split('.').slice(1).join('.').replace("https://www.", "https://").replace("http://www.", "http://");
const domain = activeSubdomain ? domainWithoutWww : window.location.hostname;
let domain = window.location.hostname;

if (activeSubdomain) {
domain = domainWithoutWww
}

if (activeSubdirectory && optsArgs.domainFromServer) {
domain = optsArgs.domainFromServer;
}


// FEATURE: Create a canonical link tag for translated pages
// e.g. https://example.com/path?lang=es
Expand Down Expand Up @@ -195,6 +242,19 @@ function extractOptionsFromScript(window, optsArgs = {
newCanonicalLinkTag.href = url.href;
}
}

if (translationMode == "subdirectory") {
if (!activeLang || (activeLang == originalLang)) {
newCanonicalLinkTag.href = `${window.location.protocol}//${domain}${window.location.pathname}`;
} else {
let url = new URL(window.location.href);
url.hostname = domain;
// url.pathname = url.pathname.replace(domainSourcePrefix, "");
url.pathname = getUnprefixedPathname(window, domainSourcePrefix, url.pathname);

newCanonicalLinkTag.href = url.href;
}
}

newCanonicalLinkTag.setAttribute('rel', 'canonical');
window.document.head.appendChild(newCanonicalLinkTag);
Expand All @@ -204,7 +264,16 @@ function extractOptionsFromScript(window, optsArgs = {
alternateLinkTag.setAttribute('rel', 'alternate');
alternateLinkTag.setAttribute('hreflang', originalLang);

const subdirectoryHref = `${window.location.protocol}//${domain}${getUnprefixedPathname(window, domainSourcePrefix, window.location.pathname)}`
const subdirUrl = new URL(window.location.href);
// cleanup from prefix and language to get the real pathname
subdirUrl.pathname = getUnprefixedPathname(window, domainSourcePrefix, subdirUrl.pathname);
subdirUrl.pathname = subdirUrl.pathname.replace(`/${activeSubdirectory}/`, '/');
const cleanPathname = subdirUrl.pathname;

// const cleanPathname = `${window.location.pathname.replace(domainSourcePrefix, "").replace(`/${originalLang}/`, '/')}`; // cleanup from language to get the real pathname
const originalPathname = getPrefixedPathname(window, domainSourcePrefix, subdirUrl.pathname) // add back the prefix
const subdirectoryHref = `${window.location.protocol}//${domain}${originalPathname}`
console.log("PATHNM2", activeSubdirectory, cleanPathname, originalPathname, subdirectoryHref, translationMode)
// append prefix to the original lang because the subdomain will be accessed without the prefix (dont append if subdirectory mode)
alternateLinkTag.href = translationMode == "subdirectory" ? subdirectoryHref : `${window.location.protocol}//${domain}${getPrefixedPathname(window, domainSourcePrefix, window.location.pathname)}`;
window.document.head.appendChild(alternateLinkTag);
Expand Down Expand Up @@ -234,17 +303,21 @@ function extractOptionsFromScript(window, optsArgs = {

// append the first slash with lang
// google.com -> google.com/en
// url.pathname = url.pathname.replace(domainSourcePrefix, "")
url.pathname = getUnprefixedPathname(window, domainSourcePrefix, url.pathname);
url.pathname = url.pathname.replace(`/${activeSubdirectory}/`, '/'); // cleanup from prefix and language to get the real pathname
let pathnames = url.pathname.split('/');
pathnames.splice(1, 0, lang);
if (lang && (lang != originalLang)) pathnames.splice(1, 0, lang);
url.pathname = pathnames.join('/');
if (lang == originalLang) url.pathname = `${domainSourcePrefix}${url.pathname}`
alternateLinkTag.href = url.href;
}

window.document.head.appendChild(alternateLinkTag);
}
}

if (!activeSubdomain || window.isWorker) {
if ((!activeSubdomain && !activeSubdirectory )|| window.isWorker) {
handleLinkTags();
}

Expand All @@ -255,7 +328,7 @@ function extractOptionsFromScript(window, optsArgs = {

if (isBrowser()) {
// subdomain may dont need to check expiration
if (!window.isWorker && !window.activeSubdomain) {
if (!window.isWorker && !window.activeSubdomain && !window.activeSubdirectory) {
try {
// get the current date
const now = new Date();
Expand Down Expand Up @@ -380,6 +453,8 @@ function extractOptionsFromScript(window, optsArgs = {

const translateSelectOptions = window.translationScriptTag.getAttribute(DATA_TRANSLATE_SELECT_OPTIONS) == "true";

const sourceOrigin = window.translationScriptTag.getAttribute(DATA_SOURCE_ORIGIN);

return {
useBrowserLanguage: !disableAutoTranslate && useBrowserLanguage,
createSelector: createSelector,
Expand All @@ -403,6 +478,7 @@ function extractOptionsFromScript(window, optsArgs = {
translationMode,
domainSourcePrefix,
customLinks,
sourceOrigin,
}
}

Expand Down
17 changes: 13 additions & 4 deletions replaceLinks.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const getUnprefixedPathname = require("./utils/translation-mode/getUnprefixedPathname");

function replaceLinks(window, {langParam, lang, translationMode, prefix}) {
function replaceLinks(window, {langParam, lang, translationMode, prefix, sourceOriginHostname}) {
// Select all anchor tags
let anchors = window.document.querySelectorAll('a');

Expand All @@ -12,7 +12,7 @@ function replaceLinks(window, {langParam, lang, translationMode, prefix}) {
let anchor = anchors[i];

// assign full url if it's relative path
if (!anchor.href.startsWith("http") && (anchor.getAttribute("href") != "#")) {
if (!anchor.href.startsWith("http") && (anchor.getAttribute("href") != "#") && !anchor.href.startsWith("tel:") && !anchor.href.startsWith("mailto:")) {
const currentUrl = new URL(window.location.href);
const fullHref = `${currentUrl.protocol}//${currentUrl.hostname}${anchor.href}`;
anchor.href = fullHref;
Expand All @@ -21,7 +21,9 @@ function replaceLinks(window, {langParam, lang, translationMode, prefix}) {
// check for en.domain.com OR www.domain.com OR domain.com
const isInternal = (anchor.hostname == `${lang}.${domain}`) || (anchor.hostname == `www.${domain}`) || anchor.hostname == window.location.hostname;

if (!isInternal) {
const isInternalForSubdirectory = translationMode == "subdirectory" && (anchor.hostname == sourceOriginHostname || anchor.hostname == `www.${sourceOriginHostname}`);

if (!isInternal && !isInternalForSubdirectory) {
// Check if the link is external
continue;
}
Expand All @@ -46,11 +48,18 @@ function replaceLinks(window, {langParam, lang, translationMode, prefix}) {
// Create a new URL object
let url = new URL(anchor.href);

if (prefix) {
url.pathname = getUnprefixedPathname(window, prefix, url.pathname);
}

url.hostname = window.location.hostname;

// append the first slash with lang
// google.com -> google.com/en
let pathnames = url.pathname.split('/');
pathnames.splice(1, 0, lang);
if (lang) pathnames.splice(1, 0, lang); // lang can be undefined for path without prefix
url.pathname = pathnames.join('/');
if (!lang) url.pathname = `${prefix}${url.pathname}`

// Update the href of the anchor tag
anchor.href = url.href;
Expand Down
7 changes: 6 additions & 1 deletion utils/configs.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function setGlobalseoOptions(window, value = {}) {

function getGlobalseoActiveLang(window) {
const options = getGlobalseoOptions(window);
if (window.paramsLang && options.translationMode != 'subdomain') {
if (window.paramsLang && options.translationMode != 'subdomain' && options.translationMode != 'subdirectory') {
return window.paramsLang;
}
if (!window.globalseoActiveLang) {
Expand All @@ -63,6 +63,11 @@ function setGlobalseoActiveLang(window, language) {
return;
}

if (options.translationMode == 'subdirectory') {
window.globalseoActiveLang = window.activeSubdirectory || options.originalLanguage
return;
}

window.globalseoActiveLang = language
}

Expand Down
16 changes: 8 additions & 8 deletions utils/selector/createLanguageSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ function addOrReplaceLangParam(window, url, lang) {

if (options.translationMode == "subdirectory") {
let pathnames = urlObj.pathname.split('/');
pathnames.splice(1, 0, lang);
if (lang && lang != options.originalLanguage) pathnames.splice(1, 0, lang);
urlObj.pathname = pathnames.join('/');
if ((!lang || lang == options.originalLanguage) && options.domainSourcePrefix) {
urlObj.pathname = `${options.domainSourcePrefix}${urlObj.pathname}`;
}
return urlObj.toString();
}

Expand Down Expand Up @@ -375,13 +378,10 @@ async function createLanguageSelect(window, optsArgs = {}) {
}
}

if (prefix && options.translationMode == "subdirectory") {
newUrl.pathname = getUnprefixedPathname(window, prefix, newUrl.pathname);
// if (options.originalLanguage == language.lang) {
// newUrl.pathname = getUnprefixedPathname(window, prefix, newUrl.pathname);
// } else {
// newUrl.pathname = getUnprefixedPathname(window, prefix, newUrl.pathname);
// }
if (options.translationMode == "subdirectory") {
if (newUrl.pathname.startsWith(`/${selectedLangLowercased}`)) {
newUrl.pathname = newUrl.pathname.replace(`/${selectedLangLowercased}`, "");
}
}

const url = isSelected ? "#" : addOrReplaceLangParam(window, newUrl.href, language.lang);
Expand Down

0 comments on commit 1f469d7

Please sign in to comment.