diff --git a/README.md b/README.md index a86a700..bef6589 100644 --- a/README.md +++ b/README.md @@ -284,10 +284,10 @@ import { Trans } from "astro-i18next/components"; #### Trans Props -| Prop name | Type (default) | Description | -| --------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -| i18nKey | string (undefined) | Internationalization key to interpolate to. Can contain the namespace by prepending it in the form 'ns:key' (depending on i18next.options.nsSeparator) | -| ns | ?string (undefined) | Namespace to use. May also be embedded in i18nKey but not recommended when used in combination with natural language keys. | +| Prop name | Type (default) | Description | +| --------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| i18nKey | ?string (undefined) | Internationalization key to interpolate to. Can contain the namespace by prepending it in the form 'ns:key' (depending on i18next.options.nsSeparator). If omitted, a key is automatically generated using the content of the element. | +| ns | ?string (undefined) | Namespace to use. May also be embedded in i18nKey but not recommended when used in combination with natural language keys. | ### LanguageSelector component diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts index c371f89..c4a68e3 100644 --- a/src/__tests__/utils.test.ts +++ b/src/__tests__/utils.test.ts @@ -1,5 +1,6 @@ import { interpolate, + createReferenceStringFromHTML, localizePath, moveBaseLanguageToFirstIndex, localizeUrl, @@ -21,6 +22,7 @@ i18next.init({ translation: { hello: "Hello!", interpolationKey: "This is a <0>super cool0> sentence!", + interpolationKeySelfClosing: "This has an image <0> here", interpolationKeyNoHTML: "This is a reference string without any HTML tags!", }, @@ -29,6 +31,7 @@ i18next.init({ translation: { hello: "Bonjour !", interpolationKey: "Ceci est une phrase <0>super cool0> !", + interpolationKeySelfClosing: "Ceci a une image <0> ici", interpolationKeyNoHTML: "Ceci est une chaîne de caractères de référence, sans aucune balise HTML !", }, @@ -75,12 +78,28 @@ describe("interpolate(...)", () => { ); }); + it("interpolates localized string with self-closing HTML tags", () => { + i18next.changeLanguage("fr"); + const referenceString = 'This has an image here'; + const interpolatedStringFR = 'Ceci a une image ici'; + + expect(interpolate("interpolationKeySelfClosing", referenceString)).toBe( + interpolatedStringFR + ); + + i18next.changeLanguage("en"); + expect(interpolate("interpolationKeySelfClosing", referenceString)).toBe( + 'This has an image here' + ); + }); + it("with an i18nKey that is undefined", () => { expect(interpolate("missingKey", referenceString)).toBe(referenceString); expect(console.warn).toHaveBeenCalled(); }); it("with no HTML tags in default slot", () => { + i18next.changeLanguage("en"); expect( interpolate( "interpolationKeyNoHTML", @@ -91,6 +110,7 @@ describe("interpolate(...)", () => { }); it("with malformed HTML tags in default slot", () => { + i18next.changeLanguage("en"); expect( interpolate( "interpolationKeyNoHTML", @@ -101,6 +121,90 @@ describe("interpolate(...)", () => { }); }); +describe("createReferenceStringFromHTML(...)", () => { + it("replaces HTML elements", () => { + expect(createReferenceStringFromHTML("Single
Nested and with attributes
Self closing tags
' + ) + ).toBe("<0>Self <1> closing tags0>"); + }); + + it("ignores allowed elements with no attributes", () => { + expect( + createReferenceStringFromHTML("Text with emphasis
") + ).toEqual("<0>Text with emphasis0>"); + + expect( + createReferenceStringFromHTML( + 'Text with emphasis
' + ) + ).toEqual("<0>Text with <1>emphasis1>0>"); + + expect( + createReferenceStringFromHTML("Text withText with emphasis${keySeparator}
` + ) + ).toEqual(`<0>Text with emphasis${keySeparator}0>`); + + expect(console.warn).toHaveBeenCalled(); + }); + + it("collapses extra whitespace", () => { + expect( + createReferenceStringFromHTML("Single \n \tThe text with image reconstructs properly
'; + const frHtml = + 'Le texte avec image se reconstruit correctement
'; + const key = createReferenceStringFromHTML(html); + const frKey = createReferenceStringFromHTML(frHtml); + i18next.addResources("en", "translation", { + [key]: key, + }); + i18next.addResources("fr", "translation", { + [key]: frKey, + }); + + i18next.changeLanguage("fr"); + expect(interpolate(key, html)).toBe(frHtml); + }); +}); + describe("localizePath(...)", () => { vi.spyOn(console, "warn"); diff --git a/src/components/Trans.astro b/src/components/Trans.astro index 420a012..d446111 100644 --- a/src/components/Trans.astro +++ b/src/components/Trans.astro @@ -1,14 +1,21 @@ --- -import { interpolate } from "../utils"; +import { interpolate, createReferenceStringFromHTML } from "../utils"; export interface Props { - i18nKey: string; + i18nKey?: string; ns?: string; } const { i18nKey, ns } = Astro.props; const referenceString = await Astro.slots.render("default"); + +let key: string; +if (typeof i18nKey === "string") { + key = i18nKey; +} else { + key = createReferenceStringFromHTML(referenceString); +} --- -