Skip to content

Add proper TS typing to @wordpress/i18n #68906

@danieliser

Description

@danieliser

What problem does this address?

Currently the @types/wordpress__i18n package is marked as deprecated and says the types are included in the @wordpress/i18n package directly.

Further there are no types, and the JSDocs types that exist now accept any in places it definitely should not. For example sprintf accepts objects for the ...args.

Lastly proper enforcement of text domains is reasonably feasible with TS.

What is your proposed solution?

I'll propose the types or do the PR if someone will quickly work it up the chain for approval.

If it can get done quickly, I'll offer to retype some of the other common packages as we have built entire TS plugins at this point and had to deal with lots of any issues.

Proposed solution

Here is my completely extendable TS wordpress/i18n functionality, including way to enforce custom text domains via addons/plugins

We are already using this currently in a custom internal @plugin/i18n package. It works great in all cases described.

Problems Solved:

✅ Proper type enforcement on __, sprintf and other args.
✅ Throws errors when objects or non string|numbers are passed.
✅ Extendable from consuming plugins/themes

/**
 * Base namespace for WordPress i18n types
 */
export namespace WordPress {
	/**
	 * Base interface for text domains.
	 */
	export interface TextDomains {
		'default': never;
	}

	// Add this type alias for better type inference
	export type TextDomain = keyof TextDomains;
}

// Export the type separately to make it easier to extend
export type TextDomain = WordPress.TextDomain;

export const sprintf = (
	format: string,
	...args: Array< string | number >
) => {
	return i18n.sprintf( format, ...args );
};

export const __ = ( text: string, domain: TextDomain ) => {
	return i18n.__( text, domain );
};

export const _x = ( text: string, context: string, domain: TextDomain ) => {
	return i18n._x( text, context, domain );
};

export const _n = (
	single: string,
	plural: string,
	number: number,
	domain: TextDomain
) => {
	return i18n._n( single, plural, number, domain );
};

export const _nx = (
	text: string,
	context: string,
	number: number,
	domain: TextDomain
) => {
	return i18n._nx( text, context, number, domain );
};

export const isRTL = () => i18n.isRTL();

And in your consumer

declare module '@wordpress/i18n' {
	namespace WordPress {
		interface TextDomains {
			'popup-maker': never;
		}
	}
}

Then when you call __( '', 'something-else' ); or sprintf( '%s %d', { text: 'string' }, { number: 0 } ) it throws errors on the second arg.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions