Skip to content

Migrate @wordpress/i18n package to TypeScript and Add TextDomain Type Safety to createI18n #70843

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 23, 2025

Conversation

USERSATOSHI
Copy link
Contributor

What?

Part of: #67691
Also Closes #68906

This PR adds support for TextDomain type-safety for i18n instance created from createI18n As well as migrate the @wordpress/i18n package to TypeScript.

Why?

This PR introduces type safety to @wordpress/i18n package as well as adds textDomain type checking.

How?

By porting the code to TypeScript

Testing Instructions

Unit Tests for Type Checking should suffice.

@USERSATOSHI USERSATOSHI changed the title feat: add TextDomain type-safety to createI18n and migrate i18n to ts Migrate wordpress/i18n package to TypeScript and Add TextDomain Type Safety to createI18n Jul 22, 2025
@USERSATOSHI USERSATOSHI changed the title Migrate wordpress/i18n package to TypeScript and Add TextDomain Type Safety to createI18n Migrate @wordpress/i18n package to TypeScript and Add TextDomain Type Safety to createI18n Jul 22, 2025
@t-hamano t-hamano added [Type] Code Quality Issues or PRs that relate to code quality [Package] i18n /packages/i18n labels Jul 23, 2025
@t-hamano t-hamano mentioned this pull request Jul 23, 2025
39 tasks
@USERSATOSHI USERSATOSHI marked this pull request as ready for review July 23, 2025 08:55
Copy link

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: USERSATOSHI <tusharbharti@git.wordpress.org>
Co-authored-by: swissspidy <swissspidy@git.wordpress.org>
Co-authored-by: im3dabasia <im3dabasia1@git.wordpress.org>
Co-authored-by: manzoorwanijk <manzoorwanijk@git.wordpress.org>
Co-authored-by: danieliser <danieliser@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Copy link
Member

@swissspidy swissspidy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome!

@swissspidy swissspidy enabled auto-merge (squash) July 23, 2025 09:08
@swissspidy swissspidy merged commit aecca70 into WordPress:trunk Jul 23, 2025
78 of 96 checks passed
@github-actions github-actions bot added this to the Gutenberg 21.4 milestone Jul 23, 2025
USERSATOSHI added a commit to USERSATOSHI/gutenberg that referenced this pull request Jul 23, 2025
…pe Safety to `createI18n` (WordPress#70843)

Co-authored-by: USERSATOSHI <tusharbharti@git.wordpress.org>
Co-authored-by: swissspidy <swissspidy@git.wordpress.org>
Co-authored-by: im3dabasia <im3dabasia1@git.wordpress.org>
Co-authored-by: manzoorwanijk <manzoorwanijk@git.wordpress.org>
Co-authored-by: danieliser <danieliser@git.wordpress.org>
@manzoorwanijk
Copy link
Member

This PR has created unnecessary friction for i18n functions. Reassigning the values to the result of i18n functions is a common and a legitimate case and this PR breaks that.

type Code = 'code-1' | 'code-2';

const errorCode: Code = 'code-1';

let message = __( 'Some default message' );

switch ( errorCode ) {
	case 'code-1':
		// This reassignment errs
		message = __( 'An error occurred' );
             // ^^^^^^^ "Type 'TranslatableText<"An error occurred">' is not assignable to type 'TranslatableText<"Some default message">'."
		break;
	default:
		message = __( 'Unknown error occurred' );
}
image

The return of i18n functions should be a string, not a string literal, because at runtime, it's not actually the English text that the type depicts. There is no use of that generic for these i18n functions.

@manzoorwanijk
Copy link
Member

IMHO, the use of that TranslatableText type in this PR was not justified or explained properly.

@USERSATOSHI
Copy link
Contributor Author

IMHO, the use of that TranslatableText type in this PR was not justified or explained properly.

Hi @manzoorwanijk, during the working of transitioning from sprintf-js to @tannin/sprintf

sprintf being a strictly typed function requires string literals to define the typings for the arguments provided to it based on the string literal in format.

PR link: #70434

All translatable functions like __, _n etc earlier returning generic string means that sprintf won't be able to generate typings for sprintf causing args typing to be. [];

the solution for this was TranslatableText type which is

export type TranslatableText< T extends string > = string & {
	/**
	 * DO NOT USE! This property _does not exist_.
	 * @private
	 */
	readonly __translatableText: T;
};

a generic string with a readonly private property that is only being used for sprintf to generate args types.

@manzoorwanijk
Copy link
Member

I understand the type-safety, but it doesn't represent the runtime behavior of those i18n functions whose return can be any translated text and not the string literal we set the type to.

@USERSATOSHI
Copy link
Contributor Author

USERSATOSHI commented Aug 7, 2025

Hmm, I believe, @swissspidy and @aduth can provide their insights on this as they would know better, what tradeoffs or how to proceed with strict typing for all translatable functions and sprintf and how they work together or how to tackle this situation.

In the meantime, I can only provide workarounds for this, which would be using union type to make TranslatableText know which messages would be in reassigns or TranslatableText<string> to allow any string but it exactly isn't the normal string reassign.

@USERSATOSHI
Copy link
Contributor Author

I understand the type-safety, but it doesn't represent the runtime behavior of those i18n functions whose return can be any translated text and not the string literal we set the type to.

@manzoorwanijk Also wanted to mention that in the #71104 mentioning #70434 would make more sense as TranslatableText being introduced in that has discussions on why it was needed. this might be useful for users who came through that issue needing a bit of background info.

As this PR only introduced conversion from JSDocs to TypeScript and addition of strict type safety in text-domains for createI18n.

@swissspidy
Copy link
Member

Best to move this discussion to the newly opened issue for better visibility.

IMHO the tradeoff with the type safety is worth it. Your example above can be simply fixed by making the variable a string.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Package] i18n /packages/i18n [Type] Code Quality Issues or PRs that relate to code quality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add proper TS typing to @wordpress/i18n
4 participants