diff --git a/README.md b/README.md
index 9eb4445..6ba5552 100644
--- a/README.md
+++ b/README.md
@@ -85,6 +85,35 @@ each node.
;
```
+## Internationalization
+
+You can render translated strings by passing a language code and a translations map. When a translation for a given key is missing, the component falls back to the original string.
+
+```jsx
+import TextLoop from "react-text-loop";
+
+const translations = {
+ en: {
+ trade: "Trade faster",
+ increase: "Increase sales",
+ },
+ zh: {
+ trade: "交易更快",
+ increase: "提高销售",
+ },
+ es: {
+ trade: "Comercia más rápido",
+ increase: "Aumenta ventas",
+ },
+};
+
+// Using string keys so translations can be applied
+;
+
+// If a translation key is missing, TextLoop will fall back to the original value
+;
+```
+
## Examples
### Fast transition
diff --git a/src/__tests__/TextLoop.test.tsx b/src/__tests__/TextLoop.test.tsx
new file mode 100644
index 0000000..09436c7
--- /dev/null
+++ b/src/__tests__/TextLoop.test.tsx
@@ -0,0 +1,45 @@
+import React from "react";
+import { render, cleanup } from "@testing-library/react";
+import TextLoop from "../components/TextLoop";
+
+afterEach(() => {
+ cleanup();
+});
+
+describe("TextLoop Internationalization", () => {
+ it("(a) default language behavior remains unchanged", () => {
+ const { getByText } = render();
+ // Should render the first item unchanged
+ getByText("First");
+ });
+
+ it("(b) switches language and updates rendered text", () => {
+ const translations = {
+ zh: { greeting: "你好", farewell: "再见" },
+ es: { greeting: "Hola", farewell: "Adiós" },
+ };
+
+ const { rerender, getByText } = render(
+
+ );
+
+ // Should display the Chinese translation for the first key
+ getByText("你好");
+
+ // Switch to Spanish and ensure text updates
+ rerender(
+
+ );
+ getByText("Hola");
+ });
+
+ it("(c) falls back to original value when translation key is missing", () => {
+ const translations = { zh: { known: "已知" } };
+ const { getByText } = render(
+
+ );
+
+ // First item has no translation; should fall back to the original value
+ getByText("unknown");
+ });
+});
diff --git a/src/components/TextLoop.tsx b/src/components/TextLoop.tsx
index 51866ba..af527a8 100644
--- a/src/components/TextLoop.tsx
+++ b/src/components/TextLoop.tsx
@@ -23,6 +23,9 @@ type Props = {
noWrap: boolean;
className?: string;
onChange?: Function;
+ // i18n additions
+ lang?: string;
+ translations?: Record>;
};
type State = {
@@ -54,7 +57,15 @@ class TextLoop extends React.PureComponent {
constructor(props: Props) {
super(props);
- const elements = React.Children.toArray(props.children);
+ const baseElements = React.Children.toArray(props.children) as (
+ JSX.Element | string
+ )[];
+
+ const elements = this.applyTranslations(
+ baseElements,
+ props.lang,
+ props.translations
+ );
this.state = {
elements,
@@ -109,10 +120,19 @@ class TextLoop extends React.PureComponent {
}
}
- if (!isEqual(prevProps.children, children)) {
+ // Update elements when children, lang, or translations change
+ if (
+ !isEqual(prevProps.children, children) ||
+ prevProps.lang !== this.props.lang ||
+ !isEqual(prevProps.translations, this.props.translations)
+ ) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({
- elements: React.Children.toArray(children),
+ elements: this.applyTranslations(
+ React.Children.toArray(children) as (JSX.Element | string)[],
+ this.props.lang,
+ this.props.translations
+ ),
});
}
}
@@ -132,6 +152,25 @@ class TextLoop extends React.PureComponent {
}
}
+ // Map strings through translations when available; keep JSX elements intact
+ applyTranslations(
+ elements: (JSX.Element | string | undefined)[],
+ lang?: string,
+ translations?: Record>
+ ): (JSX.Element | string | undefined)[] {
+ if (!lang || !translations) {
+ return elements;
+ }
+ const pack = translations[lang] || {};
+ return elements.map(el => {
+ if (typeof el === "string") {
+ const translated = pack[el];
+ return translated != null ? translated : el;
+ }
+ return el;
+ });
+ }
+
// Fade out animation
willLeave = (): { opacity: OpaqueConfig; translate: OpaqueConfig } => {
const { height } = this.getDimensions();
diff --git a/src/i18n.ts b/src/i18n.ts
new file mode 100644
index 0000000..b9103ef
--- /dev/null
+++ b/src/i18n.ts
@@ -0,0 +1,19 @@
+import { LanguageCode } from "./types";
+
+// Predefined (empty) language packs. Library users can enrich these as needed.
+export const DEFAULT_TRANSLATIONS: Record> = {
+ en: {},
+ zh: {},
+ es: {},
+};
+
+// Lightweight translation function: returns the translated string if available,
+// otherwise falls back to the provided key.
+export function t(key: string, lang: string): string {
+ const code = lang as LanguageCode;
+ const pack = DEFAULT_TRANSLATIONS[code];
+ if (pack && Object.prototype.hasOwnProperty.call(pack, key)) {
+ return pack[key];
+ }
+ return key;
+}
diff --git a/src/types.ts b/src/types.ts
new file mode 100644
index 0000000..b27aa1b
--- /dev/null
+++ b/src/types.ts
@@ -0,0 +1,3 @@
+export type LanguageCode = "en" | "zh" | "es";
+
+export type Translations = Record>;