Skip to content

Commit

Permalink
fix: remove trailing slash from localized path
Browse files Browse the repository at this point in the history
fixes #77
  • Loading branch information
yassinedoghri committed Dec 24, 2022
1 parent 9361d4b commit 6454885
Show file tree
Hide file tree
Showing 6 changed files with 1,570 additions and 762 deletions.
36 changes: 18 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,45 +72,45 @@
"dependencies": {
"@proload/core": "^0.3.3",
"@proload/plugin-tsm": "^0.2.1",
"i18next": "^22.0.6",
"i18next": "^22.4.6",
"i18next-browser-languagedetector": "^7.0.1",
"i18next-fs-backend": "^2.0.1",
"i18next-http-backend": "^2.0.2",
"i18next-fs-backend": "^2.1.1",
"i18next-http-backend": "^2.1.1",
"iso-639-1": "^2.1.15",
"locale-emoji": "^0.3.0"
},
"devDependencies": {
"@commitlint/cli": "^17.3.0",
"@commitlint/config-conventional": "^17.3.0",
"@semantic-release/changelog": "^6.0.1",
"@semantic-release/changelog": "^6.0.2",
"@semantic-release/git": "^10.0.1",
"@types/fs-extra": "^9.0.13",
"@types/yargs": "^17.0.14",
"@typescript-eslint/eslint-plugin": "^5.44.0",
"@typescript-eslint/parser": "^5.44.0",
"@vitest/coverage-c8": "^0.25.3",
"@types/yargs": "^17.0.17",
"@typescript-eslint/eslint-plugin": "^5.47.0",
"@typescript-eslint/parser": "^5.47.0",
"@vitest/coverage-c8": "^0.26.2",
"all-contributors-cli": "^6.24.0",
"astro": "1.6.11",
"astro": "1.7.2",
"cz-conventional-changelog": "^3.3.0",
"esbuild": "^0.15.15",
"esbuild": "^0.16.10",
"esbuild-plugin-fileloc": "^0.0.6",
"eslint": "^8.28.0",
"eslint": "^8.30.0",
"eslint-config-prettier": "^8.5.0",
"eslint-config-standard": "^17.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "^27.1.6",
"eslint-plugin-n": "^15.5.1",
"eslint-plugin-jest": "^27.1.7",
"eslint-plugin-n": "^15.6.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"fdir": "^5.3.0",
"fs-extra": "^10.1.0",
"fs-extra": "^11.1.0",
"husky": "^8.0.2",
"lint-staged": "^13.0.4",
"prettier": "2.8.0",
"lint-staged": "^13.1.0",
"prettier": "2.8.1",
"prettier-plugin-astro": "^0.7.0",
"semantic-release": "^19.0.5",
"typescript": "^4.9.3",
"vitest": "^0.25.3",
"typescript": "^4.9.4",
"vitest": "^0.26.2",
"yargs": "^17.6.2"
},
"lint-staged": {
Expand Down
94 changes: 47 additions & 47 deletions src/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,104 +233,104 @@ describe("localizePath(...)", () => {
i18next.changeLanguage("en");
expect(localizePath("/")).toBe("/");
expect(localizePath("/fr")).toBe("/");
expect(localizePath("/about")).toBe("/about/");
expect(localizePath("/fr/about")).toBe("/about/");
expect(localizePath("/about")).toBe("/about");
expect(localizePath("/fr/about")).toBe("/about");

i18next.changeLanguage("fr");
expect(localizePath("/")).toBe("/fr/");
expect(localizePath("/foo")).toBe("/fr/foo/");
expect(localizePath("/")).toBe("/fr");
expect(localizePath("/foo")).toBe("/fr/foo");

i18next.changeLanguage("fr-CA");
expect(localizePath("/fr/about")).toBe("/fr-CA/about/");
expect(localizePath("/about")).toBe("/fr-CA/about/");
expect(localizePath("/fr/about")).toBe("/fr-CA/about");
expect(localizePath("/about")).toBe("/fr-CA/about");
});

it("with longer paths", () => {
i18next.changeLanguage("fr");
expect(localizePath("/really/long/path")).toBe("/fr/really/long/path/");
expect(localizePath("/really/long/path")).toBe("/fr/really/long/path");
expect(localizePath("/super/huge/and/really/long/path")).toBe(
"/fr/super/huge/and/really/long/path/"
"/fr/super/huge/and/really/long/path"
);

i18next.changeLanguage("fr-CA");
expect(localizePath("/fr/really/long/path/with/locale/before")).toBe(
"/fr-CA/really/long/path/with/locale/before/"
"/fr-CA/really/long/path/with/locale/before"
);

i18next.changeLanguage("en");
expect(localizePath("/fr/really/long/path/with/locale/before")).toBe(
"/really/long/path/with/locale/before/"
"/really/long/path/with/locale/before"
);
});

it("with multiple leading slashes", () => {
i18next.changeLanguage("fr");
expect(localizePath("//fr-CA/foo")).toBe("/fr/foo/");
expect(localizePath("////fr/foo")).toBe("/fr/foo/");
expect(localizePath("//fr-CA/foo")).toBe("/fr/foo");
expect(localizePath("////fr/foo")).toBe("/fr/foo");
});

it("with an empty string as path", () => {
i18next.changeLanguage("en"); // default locale
expect(localizePath("")).toBe("/");

i18next.changeLanguage("fr");
expect(localizePath("")).toBe("/fr/");
expect(localizePath("")).toBe("/fr");
});

it("with no supplied path", () => {
i18next.changeLanguage("en"); // default locale
expect(localizePath()).toBe("/");

i18next.changeLanguage("fr");
expect(localizePath()).toBe("/fr/");
expect(localizePath()).toBe("/fr");
});

it("with an unsupported locale", () => {
i18next.changeLanguage("de");
expect(localizePath("/fr/about")).toBe("/fr/about/");
expect(localizePath("/fr/about")).toBe("/fr/about");
expect(console.warn).toHaveBeenCalled();
});

it("with base path", () => {
i18next.changeLanguage("fr");
expect(localizePath("/base", null, "/base/")).toBe("/base/fr/");
expect(localizePath("/base/about", null, "/base/")).toBe("/base/fr/about/");
expect(localizePath("/about", null, "/base/")).toBe("/base/fr/about/");
expect(localizePath("/", null, "/base/")).toBe("/base/fr/");
expect(localizePath("", null, "/base/")).toBe("/base/fr/");
expect(localizePath("/base", null, "/base/")).toBe("/base/fr");
expect(localizePath("/base/about", null, "/base/")).toBe("/base/fr/about");
expect(localizePath("/about", null, "/base/")).toBe("/base/fr/about");
expect(localizePath("/", null, "/base/")).toBe("/base/fr");
expect(localizePath("", null, "/base/")).toBe("/base/fr");

i18next.changeLanguage("en");
expect(localizePath("/base/about", null, "/base/")).toBe("/base/about/");
expect(localizePath("/about", null, "/base/")).toBe("/base/about/");
expect(localizePath("/base/about", null, "/base/")).toBe("/base/about");
expect(localizePath("/about", null, "/base/")).toBe("/base/about");
expect(localizePath("/", null, "/base/")).toBe("/base/");
expect(localizePath("", null, "/base/")).toBe("/base/");

i18next.changeLanguage("de");
expect(localizePath("/fr/about", null, "/base/")).toBe("/base/fr/about/");
expect(localizePath("base/fr/about", null, "base")).toBe("/base/fr/about/");
expect(localizePath("/fr/about", null, "/base/")).toBe("/base/fr/about");
expect(localizePath("base/fr/about", null, "base")).toBe("/base/fr/about");
expect(console.warn).toHaveBeenCalled();
});

it("with base path written weirdly", () => {
i18next.changeLanguage("fr");
expect(localizePath("", null, "base")).toBe("/base/fr/");
expect(localizePath("", null, "/base")).toBe("/base/fr/");
expect(localizePath("", null, "base/")).toBe("/base/fr/");
expect(localizePath("", null, "//base//")).toBe("/base/fr/");
expect(localizePath("", null, "/base///")).toBe("/base/fr/");
expect(localizePath("", null, "///base/")).toBe("/base/fr/");
expect(localizePath("", null, "base")).toBe("/base/fr");
expect(localizePath("", null, "/base")).toBe("/base/fr");
expect(localizePath("", null, "base/")).toBe("/base/fr");
expect(localizePath("", null, "//base//")).toBe("/base/fr");
expect(localizePath("", null, "/base///")).toBe("/base/fr");
expect(localizePath("", null, "///base/")).toBe("/base/fr");
});

it("with translated routes", () => {
i18next.changeLanguage("fr");
expect(localizePath("/fr/about")).toBe("/fr/a-propos/");
expect(localizePath("/fr-CA/about")).toBe("/fr/a-propos/");
expect(localizePath("/fr/about")).toBe("/fr/a-propos");
expect(localizePath("/fr-CA/about")).toBe("/fr/a-propos");

i18next.changeLanguage("es");
expect(localizePath("/about", "es")).toBe("/es/a-proposito/");
expect(localizePath("/about", "es")).toBe("/es/a-proposito");

i18next.changeLanguage("en");
expect(localizePath("/fr/a-propos")).toBe("/about/");
expect(localizePath("/fr/a-propos")).toBe("/about");
});

it("with showDefaultLocale set to true", () => {
Expand All @@ -349,9 +349,9 @@ describe("localizePath(...)", () => {
});

i18next.changeLanguage("en");
expect(localizePath("/fr/a-propos")).toBe("/en/about/");
expect(localizePath("/fr/")).toBe("/en/");
expect(localizePath("")).toBe("/en/");
expect(localizePath("/fr/a-propos")).toBe("/en/about");
expect(localizePath("/fr/")).toBe("/en");
expect(localizePath("")).toBe("/en");
});
});

Expand All @@ -374,17 +374,17 @@ describe("localizeUrl(...)", () => {
i18next.changeLanguage("en");
expect(localizeUrl("https://example.com/")).toBe("https://example.com/");
expect(localizeUrl("https://example.com/about")).toBe(
"https://example.com/about/"
"https://example.com/about"
);
expect(localizeUrl("https://example.com/fr/")).toBe("https://example.com/");
expect(localizeUrl("https://example.com/fr/a-propos")).toBe(
"https://example.com/about/"
"https://example.com/about"
);

i18next.changeLanguage("fr");
expect(localizeUrl("https://example.com/")).toBe("https://example.com/fr/");
expect(localizeUrl("https://example.com/")).toBe("https://example.com/fr");
expect(localizeUrl("https://example.com/about")).toBe(
"https://example.com/fr/a-propos/"
"https://example.com/fr/a-propos"
);
});

Expand All @@ -394,30 +394,30 @@ describe("localizeUrl(...)", () => {
"https://example.com/base/"
);
expect(localizeUrl("https://example.com/base/about/", null, "/base/")).toBe(
"https://example.com/base/about/"
"https://example.com/base/about"
);
expect(localizeUrl("https://example.com/fr/", null, "/base/")).toBe(
"https://example.com/base/"
);
expect(
localizeUrl("https://example.com/base/fr/about", null, "/base/")
).toBe("https://example.com/base/about/");
).toBe("https://example.com/base/about");

i18next.changeLanguage("fr");
expect(localizeUrl("https://example.com/", null, "/base/")).toBe(
"https://example.com/base/fr/"
"https://example.com/base/fr"
);
expect(localizeUrl("https://example.com/about", null, "/base/")).toBe(
"https://example.com/base/fr/about/"
"https://example.com/base/fr/about"
);
});

it("generates the correct locale given a locale", () => {
expect(localizeUrl("https://example.com/fr/a-propos", "fr")).toBe(
"https://example.com/fr/a-propos/"
"https://example.com/fr/a-propos"
);
expect(localizeUrl("https://example.com/fr/a-propos", "en")).toBe(
"https://example.com/about/"
"https://example.com/about"
);
});
});
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export default (options?: AstroI18nextOptions): AstroIntegration => {
name: "astro-i18next",
hooks: {
"astro:config:setup": async ({ config, injectScript }) => {
console.log(config);

/**
* 0. Get user config
*/
Expand Down
62 changes: 32 additions & 30 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,51 +196,53 @@ export const localizePath = (
locale = i18next.language;
}

// remove all leading slashes off of path
path = path.replace(/^\/+|\/+$/g, "");
path = path === "" ? "/" : "/" + path + "/";
let pathSegments = path.split("/").filter((segment) => segment !== "");
const baseSegments = base.split("/").filter((segment) => segment !== "");

// remove leading and trailing slashes off of base path
base = base.replace(/^\/+|\/+$/g, "");
base = base === "" ? "/" : "/" + base + "/";
if (
JSON.stringify(pathSegments).startsWith(
JSON.stringify(baseSegments).replace(/]+$/, "")
)
) {
// remove base from path
pathSegments.splice(0, baseSegments.length);
}

const { flatRoutes, showDefaultLocale, defaultLocale } = AstroI18next.config;
path = pathSegments.length === 0 ? "" : pathSegments.join("/");
base = baseSegments.length === 0 ? "/" : "/" + baseSegments.join("/") + "/";

// remove base path if found
path = path.startsWith(base) ? path.slice(base.length) : path.slice(1);
const { flatRoutes, showDefaultLocale, defaultLocale, locales } =
AstroI18next.config;

if (!(i18next.options.supportedLngs as string[]).includes(locale)) {
if (!locales.includes(locale)) {
console.warn(
`WARNING(astro-i18next): "${locale}" locale is not supported, add it to the locales in your astro config.`
);
return base + path;
return `${base}${path}`;
}

if (pathSegments.length === 0) {
if (showDefaultLocale) {
return `${base}${locale}`;
}

return locale === defaultLocale ? base : `${base}${locale}`;
}

// check if the path is not already
// check if the path is not already present in flatRoutes
if (locale === defaultLocale) {
const translatedPathKey = Object.keys(flatRoutes).find(
(key) => flatRoutes[key] + "/" === "/" + path
(key) => flatRoutes[key] === "/" + path
);
if (typeof translatedPathKey !== "undefined") {
path = translatedPathKey.replace(/^\//, "") + "/";
pathSegments = translatedPathKey
.split("/")
.filter((segment) => segment !== "");
}
}

let pathSegments = path.split("/");

if (
JSON.stringify(pathSegments) === JSON.stringify([""]) ||
JSON.stringify(pathSegments) === JSON.stringify(["", ""])
) {
if (showDefaultLocale) {
return `${base}${locale}/`;
}

return locale === defaultLocale ? base : `${base}${locale}/`;
}

// remove locale from pathSegments (if there is any)
for (const locale of i18next.options.supportedLngs as string[]) {
for (const locale of locales) {
if (pathSegments[0] === locale) {
pathSegments.shift();
break;
Expand All @@ -261,7 +263,7 @@ export const localizePath = (
localizedPath.replace(/\/$/, "")
)
) {
return flatRoutes[localizedPath.replace(/\/$/, "")] + "/";
return flatRoutes[localizedPath.replace(/\/$/, "")];
}

return localizedPath;
Expand Down Expand Up @@ -299,7 +301,7 @@ export const detectLocaleFromPath = (path: string) => {
return defaultLocale;
}

// make a copy of i18next's supportedLngs
// make a copy of supported locales
let otherLocales = [...locales];
otherLocales = otherLocales.filter((locale) => locale !== defaultLocale); // remove base locale (first index)

Expand Down
Loading

0 comments on commit 6454885

Please sign in to comment.