diff --git a/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/index.php b/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/index.php index 800d6b44bd306..6cbfd14105a3a 100644 --- a/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/index.php +++ b/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/index.php @@ -35,6 +35,14 @@ function enqueue_script_and_style() { wp_set_script_translations( 'a8c-fse-editor-site-launch-script', 'full-site-editing' ); + wp_localize_script( + 'a8c-fse-editor-site-launch-script', + 'wpcomEditorSiteLaunch', + array( + 'locale' => get_locale(), + ) + ); + wp_enqueue_style( 'a8c-fse-editor-site-launch-style', plugins_url( 'dist/editor-site-launch.css', __FILE__ ), diff --git a/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/attach-launch-sidebar.tsx b/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/attach-launch-sidebar.tsx index a7026ac9b554c..d8c497015baf2 100644 --- a/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/attach-launch-sidebar.tsx +++ b/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/attach-launch-sidebar.tsx @@ -6,6 +6,7 @@ import { useDispatch, useSelect } from '@wordpress/data'; import { registerPlugin as originalRegisterPlugin, PluginSettings } from '@wordpress/plugins'; import { doAction, hasAction } from '@wordpress/hooks'; import { LaunchContext } from '@automattic/launch'; +import { GlobalI18nProvider } from '@automattic/react-i18n'; /** * Internal dependencies @@ -41,9 +42,11 @@ registerPlugin( 'a8c-editor-site-launch', { } return ( - - - + + + + + ); }, } ); diff --git a/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/constants.ts b/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/constants.ts index 226a4d5be8506..91bdc376a52cb 100644 --- a/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/constants.ts +++ b/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/constants.ts @@ -1 +1,9 @@ export const FLOW_ID = 'gutenboarding'; + +declare global { + interface Window { + wpcomEditorSiteLaunch?: { + locale?: string; + }; + } +} diff --git a/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/launch-menu/index.tsx b/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/launch-menu/index.tsx index e76f66f1d7db7..1ab7458036c5c 100644 --- a/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/launch-menu/index.tsx +++ b/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/launch-menu/index.tsx @@ -3,7 +3,7 @@ */ import * as React from 'react'; import { useSelect, useDispatch } from '@wordpress/data'; -import { __ } from '@wordpress/i18n'; +import { useI18n } from '@automattic/react-i18n'; /** * Internal dependencies @@ -19,6 +19,8 @@ interface Props { } const LaunchMenu: React.FunctionComponent< Props > = ( { onMenuItemClick } ) => { + const { __ } = useI18n(); + const { step: currentStep } = useSelect( ( select ) => select( LAUNCH_STORE ).getState() ); const LaunchStep = useSelect( ( select ) => select( LAUNCH_STORE ).getLaunchStep() ); const LaunchSequence = useSelect( ( select ) => select( LAUNCH_STORE ).getLaunchSequence() ); @@ -34,7 +36,7 @@ const LaunchMenu: React.FunctionComponent< Props > = ( { onMenuItemClick } ) => const { setStep } = useDispatch( LAUNCH_STORE ); - const handleClick = ( step ) => { + const handleClick = ( step: LaunchStepType ) => { setStep( step ); onMenuItemClick( step ); }; diff --git a/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/launch-steps/final-step/index.tsx b/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/launch-steps/final-step/index.tsx index 6189485eee599..738d2fb6ed34a 100644 --- a/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/launch-steps/final-step/index.tsx +++ b/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/launch-steps/final-step/index.tsx @@ -5,7 +5,8 @@ import * as React from 'react'; import classnames from 'classnames'; import { ThemeProvider } from 'emotion-theming'; import { createInterpolateElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; +import { useI18n } from '@automattic/react-i18n'; +import { useI18nUtils } from '@automattic/i18n-utils'; import { useDispatch, useSelect } from '@wordpress/data'; import { Button, Tip } from '@wordpress/components'; import { Icon, check } from '@wordpress/icons'; @@ -47,6 +48,9 @@ const FinalStep: React.FunctionComponent< LaunchStepProps > = ( { onNextStep, on const { setStep } = useDispatch( LAUNCH_STORE ); + const { __ } = useI18n(); + const { localizeUrl } = useI18nUtils(); + const nameSummary = (

@@ -161,7 +165,11 @@ const FinalStep: React.FunctionComponent< LaunchStepProps > = ( { onNextStep, on

{ __( 'Questions?', 'full-site-editing' ) }{ ' ' } -

diff --git a/apps/editing-toolkit/package.json b/apps/editing-toolkit/package.json index d22a83e052f8e..c6aa65a049c66 100644 --- a/apps/editing-toolkit/package.json +++ b/apps/editing-toolkit/package.json @@ -93,6 +93,7 @@ "@automattic/launch": "^1.0.0", "@automattic/plans-grid": "^1.0.0-alpha.0", "@automattic/react-i18n": "^1.0.0-alpha.0", + "@automattic/i18n-utils": "^1.0.0", "@automattic/typography": "^1.0.0", "@babel/core": "^7.11.1", "@testing-library/jest-dom": "^5.9.0", diff --git a/client/lib/i18n-utils/test/localize-url.js b/client/lib/i18n-utils/test/localize-url.js new file mode 100644 index 0000000000000..629bf2a3cf701 --- /dev/null +++ b/client/lib/i18n-utils/test/localize-url.js @@ -0,0 +1,341 @@ +/** + * Internal dependencies + */ +import { localizeUrl } from 'calypso/lib/i18n-utils'; +// Mock only the getLocaleSlug function from i18n-calypso, and use +// original references for all the other functions +function mockFunctions() { + const original = jest.requireActual( 'i18n-calypso' ).default; + return Object.assign( Object.create( Object.getPrototypeOf( original ) ), original, { + getLocaleSlug: jest.fn( () => 'en' ), + } ); +} +jest.mock( 'i18n-calypso', () => mockFunctions() ); +const { getLocaleSlug } = jest.requireMock( 'i18n-calypso' ); + +describe( '#localizeUrl', () => { + test( 'should not change URL for `en`', () => { + [ + 'https://wordpress.com/', + 'https://de.wordpress.com/', + 'https://wordpress.com/start/', + 'https://wordpress.com/wp-login.php?action=lostpassword', + ].forEach( ( fullUrl ) => { + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( fullUrl ) ).toEqual( fullUrl ); + } ); + } ); + + test( 'should not change relative URLs', () => { + [ '/me/account', '/settings' ].forEach( ( fullUrl ) => { + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( fullUrl ) ).toEqual( fullUrl ); + getLocaleSlug.mockImplementationOnce( () => 'fr' ); + expect( localizeUrl( fullUrl ) ).toEqual( fullUrl ); + } ); + } ); + + test( 'handles invalid URLs', () => { + [ undefined, null, [], {}, { href: 'https://test' }, 'not-a-url', () => {} ].forEach( + ( fullUrl ) => { + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( fullUrl ) ).toEqual( fullUrl ); + getLocaleSlug(); // make sure to consume it. + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( fullUrl, 'fr' ) ).toEqual( fullUrl ); + getLocaleSlug(); // make sure to consume it. + getLocaleSlug.mockImplementationOnce( () => 'fr' ); + expect( localizeUrl( fullUrl ) ).toEqual( fullUrl ); + getLocaleSlug(); // make sure to consume it. + } + ); + } ); + + test( 'handles double localizeUrl', () => { + getLocaleSlug.mockImplementationOnce( () => 'de' ).mockImplementationOnce( () => 'de' ); + expect( localizeUrl( localizeUrl( 'https://automattic.com/cookies/' ) ) ).toEqual( + 'https://automattic.com/de/cookies/' + ); + getLocaleSlug(); + getLocaleSlug(); // make sure to consume it. + + getLocaleSlug.mockImplementationOnce( () => 'de' ).mockImplementationOnce( () => 'de' ); + expect( + localizeUrl( localizeUrl( 'https://en.support.wordpress.com/all-about-domains/' ) ) + ).toEqual( 'https://wordpress.com/de/support/all-about-domains/' ); + getLocaleSlug(); + getLocaleSlug(); // make sure to consume it. + + getLocaleSlug.mockImplementationOnce( () => 'de' ).mockImplementationOnce( () => 'de' ); + expect( localizeUrl( localizeUrl( 'https://wordpress.com/' ) ) ).toEqual( + 'https://de.wordpress.com/' + ); + getLocaleSlug(); + getLocaleSlug(); // make sure to consume it. + + getLocaleSlug.mockImplementationOnce( () => 'de' ).mockImplementationOnce( () => 'de' ); + expect( localizeUrl( localizeUrl( 'https://en.blog.wordpress.com/' ) ) ).toEqual( + 'https://wordpress.com/blog/' + ); + getLocaleSlug(); + getLocaleSlug(); // make sure to consume it. + } ); + + test( 'trailing slash variations', () => { + getLocaleSlug.mockImplementationOnce( () => 'de' ); + expect( localizeUrl( 'https://automattic.com/cookies/' ) ).toEqual( + 'https://automattic.com/de/cookies/' + ); + getLocaleSlug.mockImplementationOnce( () => 'de' ); + expect( localizeUrl( 'https://automattic.com/cookies' ) ).toEqual( + 'https://automattic.com/de/cookies/' + ); + } ); + + test( 'overriding locale', () => { + getLocaleSlug.mockImplementationOnce( () => 'ru' ); + expect( localizeUrl( 'https://automattic.com/cookies/', 'de' ) ).toEqual( + 'https://automattic.com/de/cookies/' + ); + getLocaleSlug(); // make sure to consume it. + + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( 'https://automattic.com/cookies', 'fr' ) ).toEqual( + 'https://automattic.com/fr/cookies/' + ); + getLocaleSlug(); // make sure to consume it. + + // Finally make sure that no overriding has stuck and it uses the getLocaleSlug() when no override is specified. + getLocaleSlug.mockImplementationOnce( () => 'ru' ); + expect( localizeUrl( 'https://automattic.com/cookies/' ) ).toEqual( + 'https://automattic.com/cookies/' + ); + getLocaleSlug(); // make sure to consume it. + + getLocaleSlug.mockImplementationOnce( () => 'de' ); + expect( localizeUrl( 'https://automattic.com/cookies/' ) ).toEqual( + 'https://automattic.com/de/cookies/' + ); + getLocaleSlug(); // make sure to consume it. + } ); + + test( 'logged-out homepage', () => { + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( 'https://wordpress.com/' ) ).toEqual( 'https://wordpress.com/' ); + getLocaleSlug.mockImplementationOnce( () => 'de' ); + expect( localizeUrl( 'https://wordpress.com/' ) ).toEqual( 'https://de.wordpress.com/' ); + getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); + expect( localizeUrl( 'https://wordpress.com/' ) ).toEqual( 'https://br.wordpress.com/' ); + getLocaleSlug.mockImplementationOnce( () => 'pl' ); + expect( localizeUrl( 'https://wordpress.com/' ) ).toEqual( 'https://wordpress.com/' ); + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( 'https://en.wordpress.com/' ) ).toEqual( 'https://wordpress.com/' ); + } ); + + test( 'blog url', () => { + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( 'https://en.blog.wordpress.com/' ) ).toEqual( + 'https://wordpress.com/blog/' + ); + getLocaleSlug.mockImplementationOnce( () => 'de' ); + expect( localizeUrl( 'https://en.blog.wordpress.com/' ) ).toEqual( + 'https://wordpress.com/blog/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); + expect( localizeUrl( 'https://en.blog.wordpress.com/' ) ).toEqual( + 'https://wordpress.com/br/blog/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pl' ); + expect( localizeUrl( 'https://en.blog.wordpress.com/' ) ).toEqual( + 'https://wordpress.com/blog/' + ); + // Don't rewrite specific blog posts + getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); + expect( localizeUrl( 'https://en.blog.wordpress.com/2020/01/01/test/' ) ).toEqual( + 'https://wordpress.com/blog/2020/01/01/test/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); + expect( localizeUrl( 'https://wordpress.com/blog/2020/01/01/test/' ) ).toEqual( + 'https://wordpress.com/blog/2020/01/01/test/' + ); + } ); + + test( 'support url', () => { + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( 'https://en.support.wordpress.com/' ) ).toEqual( + 'https://wordpress.com/support/' + ); + getLocaleSlug.mockImplementationOnce( () => 'de' ); + expect( localizeUrl( 'https://en.support.wordpress.com/' ) ).toEqual( + 'https://wordpress.com/de/support/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); + expect( localizeUrl( 'https://en.support.wordpress.com/' ) ).toEqual( + 'https://wordpress.com/br/support/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pl' ); + expect( localizeUrl( 'https://en.support.wordpress.com/' ) ).toEqual( + 'https://wordpress.com/support/' + ); + + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( 'https://en.support.wordpress.com/path/' ) ).toEqual( + 'https://wordpress.com/support/path/' + ); + getLocaleSlug.mockImplementationOnce( () => 'de' ); + expect( localizeUrl( 'https://en.support.wordpress.com/path/' ) ).toEqual( + 'https://wordpress.com/de/support/path/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); + expect( localizeUrl( 'https://en.support.wordpress.com/path/' ) ).toEqual( + 'https://wordpress.com/br/support/path/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pl' ); + expect( localizeUrl( 'https://en.support.wordpress.com/path/' ) ).toEqual( + 'https://wordpress.com/support/path/' + ); + + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( 'https://en.support.wordpress.com/' ) ).toEqual( + 'https://wordpress.com/support/' + ); + getLocaleSlug.mockImplementationOnce( () => 'de' ); + expect( localizeUrl( 'https://en.support.wordpress.com/' ) ).toEqual( + 'https://wordpress.com/de/support/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); + expect( localizeUrl( 'https://en.support.wordpress.com/' ) ).toEqual( + 'https://wordpress.com/br/support/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pl' ); + expect( localizeUrl( 'https://en.support.wordpress.com/' ) ).toEqual( + 'https://wordpress.com/support/' + ); + + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( 'https://en.support.wordpress.com/path/' ) ).toEqual( + 'https://wordpress.com/support/path/' + ); + getLocaleSlug.mockImplementationOnce( () => 'de' ); + expect( localizeUrl( 'https://en.support.wordpress.com/path/' ) ).toEqual( + 'https://wordpress.com/de/support/path/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); + expect( localizeUrl( 'https://en.support.wordpress.com/path/' ) ).toEqual( + 'https://wordpress.com/br/support/path/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pl' ); + expect( localizeUrl( 'https://en.support.wordpress.com/path/' ) ).toEqual( + 'https://wordpress.com/support/path/' + ); + } ); + + test( 'forums url', () => { + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( 'https://en.forums.wordpress.com/' ) ).toEqual( + 'https://en.forums.wordpress.com/' + ); + getLocaleSlug.mockImplementationOnce( () => 'de' ); + expect( localizeUrl( 'https://en.forums.wordpress.com/' ) ).toEqual( + 'https://de.forums.wordpress.com/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); + expect( localizeUrl( 'https://en.forums.wordpress.com/' ) ).toEqual( + 'https://br.forums.wordpress.com/' + ); + getLocaleSlug.mockImplementationOnce( () => 'th' ); + expect( localizeUrl( 'https://en.forums.wordpress.com/' ) ).toEqual( + 'https://th.forums.wordpress.com/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pl' ); + expect( localizeUrl( 'https://en.forums.wordpress.com/' ) ).toEqual( + 'https://en.forums.wordpress.com/' + ); + } ); + + test( 'privacy policy', () => { + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( 'https://automattic.com/privacy/' ) ).toEqual( + 'https://automattic.com/privacy/' + ); + getLocaleSlug.mockImplementationOnce( () => 'de' ); + expect( localizeUrl( 'https://automattic.com/privacy/' ) ).toEqual( + 'https://automattic.com/de/privacy/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pl' ); + expect( localizeUrl( 'https://automattic.com/privacy/' ) ).toEqual( + 'https://automattic.com/privacy/' + ); + } ); + + test( 'cookie policy', () => { + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( 'https://automattic.com/cookies/' ) ).toEqual( + 'https://automattic.com/cookies/' + ); + getLocaleSlug.mockImplementationOnce( () => 'de' ); + expect( localizeUrl( 'https://automattic.com/cookies/' ) ).toEqual( + 'https://automattic.com/de/cookies/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pl' ); + expect( localizeUrl( 'https://automattic.com/cookies/' ) ).toEqual( + 'https://automattic.com/cookies/' + ); + } ); + + test( 'tos', () => { + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( 'https://wordpress.com/tos/' ) ).toEqual( 'https://wordpress.com/tos/' ); + getLocaleSlug.mockImplementationOnce( () => 'de' ); + expect( localizeUrl( 'https://wordpress.com/tos/' ) ).toEqual( + 'https://de.wordpress.com/tos/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pl' ); + expect( localizeUrl( 'https://wordpress.com/tos/' ) ).toEqual( 'https://wordpress.com/tos/' ); + getLocaleSlug.mockImplementationOnce( () => 'th' ); + expect( localizeUrl( 'https://wordpress.com/tos/' ) ).toEqual( 'https://wordpress.com/tos/' ); + } ); + + test( 'jetpack', () => { + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( 'https://jetpack.com/features/comparison/' ) ).toEqual( + 'https://jetpack.com/features/comparison/' + ); + getLocaleSlug.mockImplementationOnce( () => 'de' ); + expect( localizeUrl( 'https://jetpack.com/features/comparison/' ) ).toEqual( + 'https://de.jetpack.com/features/comparison/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); + expect( localizeUrl( 'https://jetpack.com/features/comparison/' ) ).toEqual( + 'https://br.jetpack.com/features/comparison/' + ); + getLocaleSlug.mockImplementationOnce( () => 'zh-tw' ); + expect( localizeUrl( 'https://jetpack.com/features/comparison/' ) ).toEqual( + 'https://zh-tw.jetpack.com/features/comparison/' + ); + getLocaleSlug.mockImplementationOnce( () => 'pl' ); + expect( localizeUrl( 'https://jetpack.com/features/comparison/' ) ).toEqual( + 'https://jetpack.com/features/comparison/' + ); + } ); + + test( 'WordPress.com URLs', () => { + getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( 'https://wordpress.com/wp-login.php?action=lostpassword' ) ).toEqual( + 'https://wordpress.com/wp-login.php?action=lostpassword' + ); + getLocaleSlug.mockImplementationOnce( () => 'de' ); + expect( localizeUrl( 'https://wordpress.com/wp-login.php?action=lostpassword' ) ).toEqual( + 'https://de.wordpress.com/wp-login.php?action=lostpassword' + ); + } ); + + test( 'WordPress.com new style support URLs', () => { + expect( localizeUrl( 'https://wordpress.com/support/reader/', 'de' ) ).toEqual( + 'https://wordpress.com/de/support/reader/' + ); + expect( localizeUrl( 'https://wordpress.com/support/reader/#blocking-sites', 'de' ) ).toEqual( + 'https://wordpress.com/de/support/reader/#blocking-sites' + ); + } ); +} ); diff --git a/client/lib/i18n-utils/test/utils.js b/client/lib/i18n-utils/test/utils.js index 316bfc8483daf..f2a0ff7478acb 100644 --- a/client/lib/i18n-utils/test/utils.js +++ b/client/lib/i18n-utils/test/utils.js @@ -12,7 +12,6 @@ import { isDefaultLocale, removeLocaleFromPath, isLocaleVariant, - localizeUrl, canBeTranslated, getPathParts, filterLanguageRevisions, @@ -32,14 +31,6 @@ jest.mock( 'i18n-calypso', () => mockFunctions() ); const { getLocaleSlug } = jest.requireMock( 'i18n-calypso' ); describe( 'utils', () => { - // todo: remove once all usage is moved over to the @automattic/i18n-utils package version - describe( '#localizeUrl', () => { - test( 'localizeUrl is still provided by client/lib/i18n-utisl', () => { - expect( localizeUrl( 'https://wordpress.com/', 'de' ) ).toEqual( - 'https://de.wordpress.com/' - ); - } ); - } ); describe( '#isDefaultLocale', () => { test( 'should return false when a non-default locale provided', () => { expect( isDefaultLocale( 'fr' ) ).toEqual( false ); diff --git a/client/lib/i18n-utils/utils.js b/client/lib/i18n-utils/utils.js index 7269cbd76bc50..2b95e4e60d161 100644 --- a/client/lib/i18n-utils/utils.js +++ b/client/lib/i18n-utils/utils.js @@ -10,7 +10,7 @@ import i18n, { getLocaleSlug } from 'i18n-calypso'; import config from 'calypso/config'; import languages from '@automattic/languages'; import { getUrlParts } from 'calypso/lib/url/url-parts'; -export { localizeUrl } from '@automattic/i18n-utils'; +import { localizeUrl as localizeUrlActual } from '@automattic/i18n-utils'; /** * a locale can consist of three component @@ -21,6 +21,13 @@ export { localizeUrl } from '@automattic/i18n-utils'; */ const localeRegex = /^[A-Z]{2,3}(-[A-Z]{2,3})?(_[A-Z]{2,6})?$/i; +export function localizeUrl( url, locale = null ) { + return localizeUrlActual( + url, + locale || ( typeof getLocaleSlug === 'function' ? getLocaleSlug() || 'en' : 'en' ) + ); +} + export function getPathParts( path ) { // Remove trailing slash then split. If there is a trailing slash, // then the end of the array could contain an empty string. diff --git a/packages/domain-picker/package.json b/packages/domain-picker/package.json index bd521b1f7980d..92a33915eae0e 100644 --- a/packages/domain-picker/package.json +++ b/packages/domain-picker/package.json @@ -34,6 +34,7 @@ "@automattic/data-stores": "^1.0.0-alpha.1", "@automattic/onboarding": "^1.0.0", "@automattic/react-i18n": "^1.0.0-alpha.0", + "@automattic/i18n-utils": "^1.0.0", "@wordpress/base-styles": "^2.0.1", "@wordpress/components": "^10.0.5", "@wordpress/compose": "^3.19.3", diff --git a/packages/domain-picker/src/domain-picker/suggestion-item.tsx b/packages/domain-picker/src/domain-picker/suggestion-item.tsx index 9acf5326e57a6..6f49571e5a86a 100644 --- a/packages/domain-picker/src/domain-picker/suggestion-item.tsx +++ b/packages/domain-picker/src/domain-picker/suggestion-item.tsx @@ -2,12 +2,12 @@ * External dependencies */ import React, { FunctionComponent, useEffect, useState } from 'react'; -import { __ } from '@wordpress/i18n'; import { createInterpolateElement } from '@wordpress/element'; import { Spinner } from '@wordpress/components'; import { useViewportMatch } from '@wordpress/compose'; import classnames from 'classnames'; -import { sprintf } from '@wordpress/i18n'; +import { useI18n } from '@automattic/react-i18n'; +import { useI18nUtils } from '@automattic/i18n-utils'; import { v4 as uuid } from 'uuid'; import { recordTrainTracksInteract } from '@automattic/calypso-analytics'; import { Button } from '@wordpress/components'; @@ -58,6 +58,8 @@ const DomainPickerSuggestionItem: FunctionComponent< Props > = ( { selected, type = ITEM_TYPE_RADIO, } ) => { + const { __, sprintf } = useI18n(); + const { localizeUrl } = useI18nUtils(); const isMobile = useViewportMatch( 'small', '<' ); const dotPos = domain.indexOf( '.' ); @@ -162,9 +164,9 @@ const DomainPickerSuggestionItem: FunctionComponent< Props > = ( { - ), // TODO Wrap this in `localizeUrl` from lib/i18n-utils + ), } ) } diff --git a/packages/i18n-utils/package.json b/packages/i18n-utils/package.json index 7ed418d56ce2d..4bb5ad8edae47 100644 --- a/packages/i18n-utils/package.json +++ b/packages/i18n-utils/package.json @@ -22,6 +22,7 @@ "test": "yarn jest" }, "dependencies": { - "i18n-calypso": "^5.0.0" + "@automattic/react-i18n": "^1.0.0-alpha.0", + "@testing-library/react-hooks": "^3.4.2" } } diff --git a/packages/i18n-utils/src/index.ts b/packages/i18n-utils/src/index.ts index 4b81cf8b9bc1d..e95acecb9047b 100644 --- a/packages/i18n-utils/src/index.ts +++ b/packages/i18n-utils/src/index.ts @@ -1 +1,27 @@ -export { localizeUrl } from './localize-url'; +/** + * External dependencies + */ +import { useI18n } from '@automattic/react-i18n'; + +/** + * Internal dependencies + */ +import { localizeUrl } from './localize-url'; +import { Locale } from './locales'; + +export interface I18nUtils { + localizeUrl: typeof localizeUrl; +} + +export function useI18nUtils(): I18nUtils { + const { i18nLocale } = useI18n(); + + return { + localizeUrl: ( fullUrl: string, locale?: Locale ) => { + if ( locale ) { + return localizeUrl( fullUrl, locale ); + } + return localizeUrl( fullUrl, i18nLocale ); + }, + }; +} diff --git a/packages/i18n-utils/src/localize-url.ts b/packages/i18n-utils/src/localize-url.ts index 566bc87547540..d8241dc6e74df 100644 --- a/packages/i18n-utils/src/localize-url.ts +++ b/packages/i18n-utils/src/localize-url.ts @@ -1,8 +1,6 @@ /** * Internal dependencies */ -import { getLocaleSlug } from 'i18n-calypso'; - import { localesWithBlog, localesWithPrivacyPolicy, @@ -86,10 +84,7 @@ const urlLocalizationMapping: UrlLocalizationMapping = { 'wordpress.com': setLocalizedUrlHost( 'wordpress.com', magnificentNonEnLocales ), }; -export function localizeUrl( fullUrl: string, toLocale?: Locale ): string { - const locale = - toLocale || ( typeof getLocaleSlug === 'function' ? getLocaleSlug() || 'en' : 'en' ); - +export function localizeUrl( fullUrl: string, locale: Locale ): string { const url = new URL( String( fullUrl ), INVALID_URL ); // Ignore and passthrough /relative/urls that have no host specified diff --git a/packages/i18n-utils/test/localize-url.js b/packages/i18n-utils/test/localize-url.js index a4b097687376b..f9b55bc5e07cf 100644 --- a/packages/i18n-utils/test/localize-url.js +++ b/packages/i18n-utils/test/localize-url.js @@ -1,20 +1,49 @@ +/* eslint-disable no-shadow -- shadowing localizeUrl makes tests readable */ + +/** + * External dependencies + */ +import { renderHook } from '@testing-library/react-hooks'; + /** * Internal dependencies */ -import { localizeUrl } from '../src'; +import { useI18nUtils } from '../src'; -// Mock only the getLocaleSlug function from i18n-calypso, and use -// original references for all the other functions -function mockFunctions() { - const original = jest.requireActual( 'i18n-calypso' ).default; +jest.mock( '@automattic/react-i18n', () => { + const original = jest.requireActual( '@automattic/react-i18n' ); return Object.assign( Object.create( Object.getPrototypeOf( original ) ), original, { - getLocaleSlug: jest.fn( () => 'en' ), + useI18n: jest.fn( () => ( { i18nLocale: 'en' } ) ), } ); -} -jest.mock( 'i18n-calypso', () => mockFunctions() ); -const { getLocaleSlug } = jest.requireMock( 'i18n-calypso' ); +} ); +const { useI18n } = jest.requireMock( '@automattic/react-i18n' ); describe( '#localizeUrl', () => { + function useLocalizeUrl( locale = 'en' ) { + useI18n.mockImplementation( () => ( { i18nLocale: locale } ) ); + const { + result: { + current: { localizeUrl }, + }, + } = renderHook( () => useI18nUtils() ); // eslint-disable-line react-hooks/rules-of-hooks -- being called within renderHook context + return localizeUrl; + } + + const localizeUrl = useLocalizeUrl( 'en' ); + + test( 'should use react-i18n useI18n hook for locale fallback', () => { + let localizeUrl; + + localizeUrl = useLocalizeUrl( 'pt-br' ); + expect( localizeUrl( 'https://en.forums.wordpress.com/' ) ).toEqual( + 'https://br.forums.wordpress.com/' + ); + localizeUrl = useLocalizeUrl( 'en' ); + expect( localizeUrl( 'https://en.forums.wordpress.com/' ) ).toEqual( + 'https://en.forums.wordpress.com/' + ); + } ); + test( 'should not change URL for `en`', () => { [ 'https://wordpress.com/', @@ -22,311 +51,222 @@ describe( '#localizeUrl', () => { 'https://wordpress.com/start/', 'https://wordpress.com/wp-login.php?action=lostpassword', ].forEach( ( fullUrl ) => { - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( fullUrl ) ).toEqual( fullUrl ); + expect( localizeUrl( fullUrl, 'en' ) ).toEqual( fullUrl ); } ); } ); test( 'should not change relative URLs', () => { [ '/me/account', '/settings' ].forEach( ( fullUrl ) => { - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( fullUrl ) ).toEqual( fullUrl ); - getLocaleSlug.mockImplementationOnce( () => 'fr' ); - expect( localizeUrl( fullUrl ) ).toEqual( fullUrl ); + expect( localizeUrl( fullUrl, 'en' ) ).toEqual( fullUrl ); + expect( localizeUrl( fullUrl, 'fr' ) ).toEqual( fullUrl ); } ); } ); test( 'handles invalid URLs', () => { [ undefined, null, [], {}, { href: 'https://test' }, 'not-a-url', () => {} ].forEach( ( fullUrl ) => { - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( fullUrl ) ).toEqual( fullUrl ); - getLocaleSlug(); // make sure to consume it. - getLocaleSlug.mockImplementationOnce( () => 'en' ); + expect( localizeUrl( fullUrl, 'en' ) ).toEqual( fullUrl ); expect( localizeUrl( fullUrl, 'fr' ) ).toEqual( fullUrl ); - getLocaleSlug(); // make sure to consume it. - getLocaleSlug.mockImplementationOnce( () => 'fr' ); - expect( localizeUrl( fullUrl ) ).toEqual( fullUrl ); - getLocaleSlug(); // make sure to consume it. } ); } ); test( 'handles double localizeUrl', () => { - getLocaleSlug.mockImplementationOnce( () => 'de' ).mockImplementationOnce( () => 'de' ); - expect( localizeUrl( localizeUrl( 'https://automattic.com/cookies/' ) ) ).toEqual( + expect( localizeUrl( localizeUrl( 'https://automattic.com/cookies/', 'de' ), 'de' ) ).toEqual( 'https://automattic.com/de/cookies/' ); - getLocaleSlug(); - getLocaleSlug(); // make sure to consume it. - - getLocaleSlug.mockImplementationOnce( () => 'de' ).mockImplementationOnce( () => 'de' ); expect( - localizeUrl( localizeUrl( 'https://en.support.wordpress.com/all-about-domains/' ) ) + localizeUrl( + localizeUrl( 'https://en.support.wordpress.com/all-about-domains/', 'de' ), + 'de' + ) ).toEqual( 'https://wordpress.com/de/support/all-about-domains/' ); - getLocaleSlug(); - getLocaleSlug(); // make sure to consume it. - getLocaleSlug.mockImplementationOnce( () => 'de' ).mockImplementationOnce( () => 'de' ); - expect( localizeUrl( localizeUrl( 'https://wordpress.com/' ) ) ).toEqual( + expect( localizeUrl( localizeUrl( 'https://wordpress.com/', 'de' ), 'de' ) ).toEqual( 'https://de.wordpress.com/' ); - getLocaleSlug(); - getLocaleSlug(); // make sure to consume it. - - getLocaleSlug.mockImplementationOnce( () => 'de' ).mockImplementationOnce( () => 'de' ); - expect( localizeUrl( localizeUrl( 'https://en.blog.wordpress.com/' ) ) ).toEqual( + expect( localizeUrl( localizeUrl( 'https://en.blog.wordpress.com/', 'de' ), 'de' ) ).toEqual( 'https://wordpress.com/blog/' ); - getLocaleSlug(); - getLocaleSlug(); // make sure to consume it. } ); test( 'trailing slash variations', () => { - getLocaleSlug.mockImplementationOnce( () => 'de' ); - expect( localizeUrl( 'https://automattic.com/cookies/' ) ).toEqual( - 'https://automattic.com/de/cookies/' - ); - getLocaleSlug.mockImplementationOnce( () => 'de' ); - expect( localizeUrl( 'https://automattic.com/cookies' ) ).toEqual( - 'https://automattic.com/de/cookies/' - ); - } ); - - test( 'overriding locale', () => { - getLocaleSlug.mockImplementationOnce( () => 'ru' ); expect( localizeUrl( 'https://automattic.com/cookies/', 'de' ) ).toEqual( 'https://automattic.com/de/cookies/' ); - getLocaleSlug(); // make sure to consume it. - - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( 'https://automattic.com/cookies', 'fr' ) ).toEqual( - 'https://automattic.com/fr/cookies/' - ); - getLocaleSlug(); // make sure to consume it. - - // Finally make sure that no overriding has stuck and it uses the getLocaleSlug() when no override is specified. - getLocaleSlug.mockImplementationOnce( () => 'ru' ); - expect( localizeUrl( 'https://automattic.com/cookies/' ) ).toEqual( - 'https://automattic.com/cookies/' - ); - getLocaleSlug(); // make sure to consume it. - - getLocaleSlug.mockImplementationOnce( () => 'de' ); - expect( localizeUrl( 'https://automattic.com/cookies/' ) ).toEqual( + expect( localizeUrl( 'https://automattic.com/cookies', 'de' ) ).toEqual( 'https://automattic.com/de/cookies/' ); - getLocaleSlug(); // make sure to consume it. } ); test( 'logged-out homepage', () => { - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( 'https://wordpress.com/' ) ).toEqual( 'https://wordpress.com/' ); - getLocaleSlug.mockImplementationOnce( () => 'de' ); - expect( localizeUrl( 'https://wordpress.com/' ) ).toEqual( 'https://de.wordpress.com/' ); - getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); - expect( localizeUrl( 'https://wordpress.com/' ) ).toEqual( 'https://br.wordpress.com/' ); - getLocaleSlug.mockImplementationOnce( () => 'pl' ); - expect( localizeUrl( 'https://wordpress.com/' ) ).toEqual( 'https://wordpress.com/' ); - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( 'https://en.wordpress.com/' ) ).toEqual( 'https://wordpress.com/' ); + expect( localizeUrl( 'https://wordpress.com/', 'en' ) ).toEqual( 'https://wordpress.com/' ); + expect( localizeUrl( 'https://wordpress.com/', 'de' ) ).toEqual( 'https://de.wordpress.com/' ); + expect( localizeUrl( 'https://wordpress.com/', 'pt-br' ) ).toEqual( + 'https://br.wordpress.com/' + ); + expect( localizeUrl( 'https://wordpress.com/', 'pl' ) ).toEqual( 'https://wordpress.com/' ); + + expect( localizeUrl( 'https://en.wordpress.com/', 'en' ) ).toEqual( 'https://wordpress.com/' ); } ); test( 'blog url', () => { - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( 'https://en.blog.wordpress.com/' ) ).toEqual( + expect( localizeUrl( 'https://en.blog.wordpress.com/', 'en' ) ).toEqual( 'https://wordpress.com/blog/' ); - getLocaleSlug.mockImplementationOnce( () => 'de' ); - expect( localizeUrl( 'https://en.blog.wordpress.com/' ) ).toEqual( + expect( localizeUrl( 'https://en.blog.wordpress.com/', 'de' ) ).toEqual( 'https://wordpress.com/blog/' ); - getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); - expect( localizeUrl( 'https://en.blog.wordpress.com/' ) ).toEqual( + expect( localizeUrl( 'https://en.blog.wordpress.com/', 'pt-br' ) ).toEqual( 'https://wordpress.com/br/blog/' ); - getLocaleSlug.mockImplementationOnce( () => 'pl' ); - expect( localizeUrl( 'https://en.blog.wordpress.com/' ) ).toEqual( + expect( localizeUrl( 'https://en.blog.wordpress.com/', 'pl' ) ).toEqual( 'https://wordpress.com/blog/' ); // Don't rewrite specific blog posts - getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); - expect( localizeUrl( 'https://en.blog.wordpress.com/2020/01/01/test/' ) ).toEqual( + expect( localizeUrl( 'https://en.blog.wordpress.com/2020/01/01/test/', 'pt-br' ) ).toEqual( 'https://wordpress.com/blog/2020/01/01/test/' ); - getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); - expect( localizeUrl( 'https://wordpress.com/blog/2020/01/01/test/' ) ).toEqual( + expect( localizeUrl( 'https://wordpress.com/blog/2020/01/01/test/', 'pt-br' ) ).toEqual( 'https://wordpress.com/blog/2020/01/01/test/' ); } ); test( 'support url', () => { - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( 'https://en.support.wordpress.com/' ) ).toEqual( + expect( localizeUrl( 'https://en.support.wordpress.com/', 'en' ) ).toEqual( 'https://wordpress.com/support/' ); - getLocaleSlug.mockImplementationOnce( () => 'de' ); - expect( localizeUrl( 'https://en.support.wordpress.com/' ) ).toEqual( + expect( localizeUrl( 'https://en.support.wordpress.com/', 'de' ) ).toEqual( 'https://wordpress.com/de/support/' ); - getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); - expect( localizeUrl( 'https://en.support.wordpress.com/' ) ).toEqual( + expect( localizeUrl( 'https://en.support.wordpress.com/', 'pt-br' ) ).toEqual( 'https://wordpress.com/br/support/' ); - getLocaleSlug.mockImplementationOnce( () => 'pl' ); - expect( localizeUrl( 'https://en.support.wordpress.com/' ) ).toEqual( + expect( localizeUrl( 'https://en.support.wordpress.com/', 'pl' ) ).toEqual( 'https://wordpress.com/support/' ); - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( 'https://en.support.wordpress.com/path/' ) ).toEqual( + expect( localizeUrl( 'https://en.support.wordpress.com/path/', 'en' ) ).toEqual( 'https://wordpress.com/support/path/' ); - getLocaleSlug.mockImplementationOnce( () => 'de' ); - expect( localizeUrl( 'https://en.support.wordpress.com/path/' ) ).toEqual( + expect( localizeUrl( 'https://en.support.wordpress.com/path/', 'de' ) ).toEqual( 'https://wordpress.com/de/support/path/' ); - getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); - expect( localizeUrl( 'https://en.support.wordpress.com/path/' ) ).toEqual( + expect( localizeUrl( 'https://en.support.wordpress.com/path/', 'pt-br' ) ).toEqual( 'https://wordpress.com/br/support/path/' ); - getLocaleSlug.mockImplementationOnce( () => 'pl' ); - expect( localizeUrl( 'https://en.support.wordpress.com/path/' ) ).toEqual( + expect( localizeUrl( 'https://en.support.wordpress.com/path/', 'pl' ) ).toEqual( 'https://wordpress.com/support/path/' ); - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( 'https://en.support.wordpress.com/' ) ).toEqual( + expect( localizeUrl( 'https://en.support.wordpress.com/', 'en' ) ).toEqual( 'https://wordpress.com/support/' ); - getLocaleSlug.mockImplementationOnce( () => 'de' ); - expect( localizeUrl( 'https://en.support.wordpress.com/' ) ).toEqual( + expect( localizeUrl( 'https://en.support.wordpress.com/', 'de' ) ).toEqual( 'https://wordpress.com/de/support/' ); - getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); - expect( localizeUrl( 'https://en.support.wordpress.com/' ) ).toEqual( + + expect( localizeUrl( 'https://en.support.wordpress.com/', 'pt-br' ) ).toEqual( 'https://wordpress.com/br/support/' ); - getLocaleSlug.mockImplementationOnce( () => 'pl' ); - expect( localizeUrl( 'https://en.support.wordpress.com/' ) ).toEqual( + expect( localizeUrl( 'https://en.support.wordpress.com/', 'pl' ) ).toEqual( 'https://wordpress.com/support/' ); - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( 'https://en.support.wordpress.com/path/' ) ).toEqual( + expect( localizeUrl( 'https://en.support.wordpress.com/path/', 'en' ) ).toEqual( 'https://wordpress.com/support/path/' ); - getLocaleSlug.mockImplementationOnce( () => 'de' ); - expect( localizeUrl( 'https://en.support.wordpress.com/path/' ) ).toEqual( + expect( localizeUrl( 'https://en.support.wordpress.com/path/', 'de' ) ).toEqual( 'https://wordpress.com/de/support/path/' ); - getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); - expect( localizeUrl( 'https://en.support.wordpress.com/path/' ) ).toEqual( + expect( localizeUrl( 'https://en.support.wordpress.com/path/', 'pt-br' ) ).toEqual( 'https://wordpress.com/br/support/path/' ); - getLocaleSlug.mockImplementationOnce( () => 'pl' ); - expect( localizeUrl( 'https://en.support.wordpress.com/path/' ) ).toEqual( + expect( localizeUrl( 'https://en.support.wordpress.com/path/', 'pl' ) ).toEqual( 'https://wordpress.com/support/path/' ); } ); test( 'forums url', () => { - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( 'https://en.forums.wordpress.com/' ) ).toEqual( + expect( localizeUrl( 'https://en.forums.wordpress.com/', 'en' ) ).toEqual( 'https://en.forums.wordpress.com/' ); - getLocaleSlug.mockImplementationOnce( () => 'de' ); - expect( localizeUrl( 'https://en.forums.wordpress.com/' ) ).toEqual( + expect( localizeUrl( 'https://en.forums.wordpress.com/', 'de' ) ).toEqual( 'https://de.forums.wordpress.com/' ); - getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); - expect( localizeUrl( 'https://en.forums.wordpress.com/' ) ).toEqual( + expect( localizeUrl( 'https://en.forums.wordpress.com/', 'pt-br' ) ).toEqual( 'https://br.forums.wordpress.com/' ); - getLocaleSlug.mockImplementationOnce( () => 'th' ); - expect( localizeUrl( 'https://en.forums.wordpress.com/' ) ).toEqual( + expect( localizeUrl( 'https://en.forums.wordpress.com/', 'th' ) ).toEqual( 'https://th.forums.wordpress.com/' ); - getLocaleSlug.mockImplementationOnce( () => 'pl' ); - expect( localizeUrl( 'https://en.forums.wordpress.com/' ) ).toEqual( + expect( localizeUrl( 'https://en.forums.wordpress.com/', 'pl' ) ).toEqual( 'https://en.forums.wordpress.com/' ); } ); test( 'privacy policy', () => { - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( 'https://automattic.com/privacy/' ) ).toEqual( + expect( localizeUrl( 'https://automattic.com/privacy/', 'en' ) ).toEqual( 'https://automattic.com/privacy/' ); - getLocaleSlug.mockImplementationOnce( () => 'de' ); - expect( localizeUrl( 'https://automattic.com/privacy/' ) ).toEqual( + expect( localizeUrl( 'https://automattic.com/privacy/', 'de' ) ).toEqual( 'https://automattic.com/de/privacy/' ); - getLocaleSlug.mockImplementationOnce( () => 'pl' ); - expect( localizeUrl( 'https://automattic.com/privacy/' ) ).toEqual( + expect( localizeUrl( 'https://automattic.com/privacy/', 'pl' ) ).toEqual( 'https://automattic.com/privacy/' ); } ); test( 'cookie policy', () => { - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( 'https://automattic.com/cookies/' ) ).toEqual( + expect( localizeUrl( 'https://automattic.com/cookies/', 'en' ) ).toEqual( 'https://automattic.com/cookies/' ); - getLocaleSlug.mockImplementationOnce( () => 'de' ); - expect( localizeUrl( 'https://automattic.com/cookies/' ) ).toEqual( + expect( localizeUrl( 'https://automattic.com/cookies/', 'de' ) ).toEqual( 'https://automattic.com/de/cookies/' ); - getLocaleSlug.mockImplementationOnce( () => 'pl' ); - expect( localizeUrl( 'https://automattic.com/cookies/' ) ).toEqual( + expect( localizeUrl( 'https://automattic.com/cookies/', 'pl' ) ).toEqual( 'https://automattic.com/cookies/' ); } ); test( 'tos', () => { - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( 'https://wordpress.com/tos/' ) ).toEqual( 'https://wordpress.com/tos/' ); - getLocaleSlug.mockImplementationOnce( () => 'de' ); - expect( localizeUrl( 'https://wordpress.com/tos/' ) ).toEqual( + expect( localizeUrl( 'https://wordpress.com/tos/', 'en' ) ).toEqual( + 'https://wordpress.com/tos/' + ); + expect( localizeUrl( 'https://wordpress.com/tos/', 'de' ) ).toEqual( 'https://de.wordpress.com/tos/' ); - getLocaleSlug.mockImplementationOnce( () => 'pl' ); - expect( localizeUrl( 'https://wordpress.com/tos/' ) ).toEqual( 'https://wordpress.com/tos/' ); - getLocaleSlug.mockImplementationOnce( () => 'th' ); - expect( localizeUrl( 'https://wordpress.com/tos/' ) ).toEqual( 'https://wordpress.com/tos/' ); + expect( localizeUrl( 'https://wordpress.com/tos/', 'pl' ) ).toEqual( + 'https://wordpress.com/tos/' + ); + expect( localizeUrl( 'https://wordpress.com/tos/', 'th' ) ).toEqual( + 'https://wordpress.com/tos/' + ); } ); test( 'jetpack', () => { - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( 'https://jetpack.com/features/comparison/' ) ).toEqual( + expect( localizeUrl( 'https://jetpack.com/features/comparison/', 'en' ) ).toEqual( 'https://jetpack.com/features/comparison/' ); - getLocaleSlug.mockImplementationOnce( () => 'de' ); - expect( localizeUrl( 'https://jetpack.com/features/comparison/' ) ).toEqual( + expect( localizeUrl( 'https://jetpack.com/features/comparison/', 'de' ) ).toEqual( 'https://de.jetpack.com/features/comparison/' ); - getLocaleSlug.mockImplementationOnce( () => 'pt-br' ); - expect( localizeUrl( 'https://jetpack.com/features/comparison/' ) ).toEqual( + expect( localizeUrl( 'https://jetpack.com/features/comparison/', 'pt-br' ) ).toEqual( 'https://br.jetpack.com/features/comparison/' ); - getLocaleSlug.mockImplementationOnce( () => 'zh-tw' ); - expect( localizeUrl( 'https://jetpack.com/features/comparison/' ) ).toEqual( + expect( localizeUrl( 'https://jetpack.com/features/comparison/', 'zh-tw' ) ).toEqual( 'https://zh-tw.jetpack.com/features/comparison/' ); - getLocaleSlug.mockImplementationOnce( () => 'pl' ); - expect( localizeUrl( 'https://jetpack.com/features/comparison/' ) ).toEqual( + expect( localizeUrl( 'https://jetpack.com/features/comparison/', 'pl' ) ).toEqual( 'https://jetpack.com/features/comparison/' ); } ); test( 'WordPress.com URLs', () => { - getLocaleSlug.mockImplementationOnce( () => 'en' ); - expect( localizeUrl( 'https://wordpress.com/wp-login.php?action=lostpassword' ) ).toEqual( + expect( localizeUrl( 'https://wordpress.com/wp-login.php?action=lostpassword', 'en' ) ).toEqual( 'https://wordpress.com/wp-login.php?action=lostpassword' ); - getLocaleSlug.mockImplementationOnce( () => 'de' ); - expect( localizeUrl( 'https://wordpress.com/wp-login.php?action=lostpassword' ) ).toEqual( + expect( localizeUrl( 'https://wordpress.com/wp-login.php?action=lostpassword', 'de' ) ).toEqual( 'https://de.wordpress.com/wp-login.php?action=lostpassword' ); } ); diff --git a/packages/react-i18n/src/index.tsx b/packages/react-i18n/src/index.tsx index 1c064fd22f603..9dff11166a878 100644 --- a/packages/react-i18n/src/index.tsx +++ b/packages/react-i18n/src/index.tsx @@ -2,7 +2,7 @@ * External dependencies */ import * as React from 'react'; -import { createI18n, I18n, LocaleData } from '@wordpress/i18n'; +import { createI18n, I18n, LocaleData, __, _n, _nx, _x, sprintf, isRTL } from '@wordpress/i18n'; import { createHigherOrderComponent } from '@wordpress/compose'; export interface I18nReact { @@ -10,6 +10,7 @@ export interface I18nReact { _n: I18n[ '_n' ]; _nx: I18n[ '_nx' ]; _x: I18n[ '_x' ]; + sprintf: typeof sprintf; isRTL: I18n[ 'isRTL' ]; i18nLocale: string; } @@ -27,6 +28,29 @@ export const I18nProvider: React.FunctionComponent< Props > = ( { children, loca return { children }; }; +interface GlobalI18nProviderProps { + localeSlug: string; +} + +export const GlobalI18nProvider: React.FunctionComponent< GlobalI18nProviderProps > = ( { + children, + localeSlug, +} ) => { + const contextValue = React.useMemo< I18nReact >( + () => ( { + __, + _n, + _nx, + _x, + sprintf, + isRTL, + i18nLocale: localeSlug, + } ), + [ localeSlug ] + ); + return { children }; +}; + /** * React hook providing i18n translate functions * @@ -77,6 +101,7 @@ function makeContextValue( localeData?: LocaleData ): I18nReact { _nx: i18n._nx.bind( i18n ), _x: i18n._x.bind( i18n ), isRTL: i18n.isRTL.bind( i18n ), + sprintf, i18nLocale, }; } diff --git a/yarn.lock b/yarn.lock index d3521f131d6d0..4492a559f8f83 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1269,6 +1269,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.5.4": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" + integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.4", "@babel/template@^7.3.3", "@babel/template@^7.4.0", "@babel/template@^7.7.4", "@babel/template@^7.8.6": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" @@ -3719,6 +3726,14 @@ lodash "^4.17.15" redent "^3.0.0" +"@testing-library/react-hooks@^3.4.2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-3.4.2.tgz#8deb94f7684e0d896edd84a4c90e5b79a0810bc2" + integrity sha512-RfPG0ckOzUIVeIqlOc1YztKgFW+ON8Y5xaSPbiBkfj9nMkkiLhLeBXT5icfPX65oJV/zCZu4z8EVnUc6GY9C5A== + dependencies: + "@babel/runtime" "^7.5.4" + "@types/testing-library__react-hooks" "^3.4.0" + "@testing-library/react@^10.0.5": version "10.0.5" resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-10.0.5.tgz#78c77a1d583777615bf0a8b8d7550b876de9f409" @@ -4064,6 +4079,13 @@ dependencies: "@types/react" "*" +"@types/react-test-renderer@*": + version "16.9.3" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.9.3.tgz#96bab1860904366f4e848b739ba0e2f67bcae87e" + integrity sha512-wJ7IlN5NI82XMLOyHSa+cNN4Z0I+8/YaLl04uDgcZ+W+ExWCmCiVTLT/7fRNqzy4OhStZcUwIqLNF7q+AdW43Q== + dependencies: + "@types/react" "*" + "@types/react-textarea-autosize@^4.3.3": version "4.3.5" resolved "https://registry.yarnpkg.com/@types/react-textarea-autosize/-/react-textarea-autosize-4.3.5.tgz#6c4d2753fa1864c98c0b2b517f67bb1f6e4c46de" @@ -4129,6 +4151,13 @@ dependencies: "@types/jest" "*" +"@types/testing-library__react-hooks@^3.4.0": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@types/testing-library__react-hooks/-/testing-library__react-hooks-3.4.1.tgz#b8d7311c6c1f7db3103e94095fe901f8fef6e433" + integrity sha512-G4JdzEcq61fUyV6wVW9ebHWEiLK2iQvaBuCHHn9eMSbZzVh4Z4wHnUGIvQOYCCYeu5DnUtFyNYuAAgbSaO/43Q== + dependencies: + "@types/react-test-renderer" "*" + "@types/testing-library__react@^10.0.1": version "10.0.1" resolved "https://registry.yarnpkg.com/@types/testing-library__react/-/testing-library__react-10.0.1.tgz#92bb4a02394bf44428e35f1da2970ed77f803593"