Skip to content

Commit

Permalink
add pluralization to useI18n hook
Browse files Browse the repository at this point in the history
  • Loading branch information
Haberkamp committed Oct 31, 2024
1 parent 113238a commit 4917db0
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 5 deletions.
60 changes: 60 additions & 0 deletions packages/component-library/src/composables/useI18n.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,64 @@ describe("useI18n", () => {

expect(screen.getByText("Hello, {name}!")).toBeInTheDocument();
});

it("translates the singular version", () => {
render({
setup() {
const { t } = useI18n({
messages: { en: { apple: "apple | apples" } },
});

return { t };
},
template: "{{ t('apple', { n: 1 }) }}",
});

expect(screen.getByText("apple")).toBeInTheDocument();
});

it("translates the pluralized version", () => {
render({
setup() {
const { t } = useI18n({
messages: { en: { apple: "apple | apples" } },
});

return { t };
},
template: "{{ t('apple', { n: 2 }) }}",
});

expect(screen.getByText("apples")).toBeInTheDocument;
});

it("translates the 'none' version", () => {
render({
setup() {
const { t } = useI18n({
messages: { en: { apple: "no apple | apple | apples" } },
});

return { t };
},
template: "{{ t('apple', { n: 0 }) }}",
});

expect(screen.getByText("no apple")).toBeInTheDocument();
});

it("translates the pluralized version with custom values", () => {
render({
setup() {
const { t } = useI18n({
messages: { en: { apple: "no apple | apple | {n} apples" } },
});

return { t };
},
template: "{{ t('apple', { n: 3 }) }}",
});

expect(screen.getByText("3 apples")).toBeInTheDocument();
});
});
35 changes: 30 additions & 5 deletions packages/component-library/src/composables/useI18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ const defaultI18nState = {

const i18nInjectionKey = Symbol("mt-i18n");

function limit(value: number, max: number) {
return Math.min(value, max);
}

export function provideI18n(locale: string = "en") {
const state = {
...defaultI18nState,
Expand All @@ -45,16 +49,37 @@ export function useI18n({ messages }: Options) {
const i18n = inject(i18nInjectionKey, defaultI18nState);

function translate(path: string, customValues: Record<string, string | number> = {}): string {
function resolveCustomKeys(translation: string) {
return translation.replace(CUSTOM_VALUE_REGEX, (match: string, key: string) => {
return Object.prototype.hasOwnProperty.call(customValues, key)
? customValues[key].toString()
: match;
});
}

const translation =
get(messages, `${i18n.locale}.${path}`) || get(messages, `${i18n.defaultLocale}.${path}`);

if (!translation) return path;

return translation.replace(CUSTOM_VALUE_REGEX, (match: string, key: string) => {
return Object.prototype.hasOwnProperty.call(customValues, key)
? customValues[key].toString()
: match;
});
const canBePluralized = /\|/.test(translation);
if (canBePluralized) {
if (typeof customValues.n !== "number")
throw new Error('The "n" key is required for pluralization');

const versions = translation.split("|");

// "no apple | apple | apples"; The first version is the zero form
const includesZeroForm = versions.length === 3;

const index = limit(includesZeroForm ? customValues.n : customValues.n - 1, 2);
const resolvedTranslation = versions.at(index);
if (!resolvedTranslation) return path;

return resolveCustomKeys(resolvedTranslation);
}

return resolveCustomKeys(translation);
}

return { t: translate };
Expand Down

0 comments on commit 4917db0

Please sign in to comment.