Skip to content

feat: Internationlization #120

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 48 commits into from
May 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
95b456c
build: Add node-fetch dependency
dcalhoun Apr 30, 2025
e4c7b9a
feat: Add locale configuration
dcalhoun May 1, 2025
cbddc69
feat: Configure locale
dcalhoun May 1, 2025
38b30f1
build: Add download translations script
dcalhoun May 1, 2025
a42af38
task: Add logger utility
dcalhoun May 1, 2025
fd4c654
refactor: Utilize logger utility
dcalhoun May 1, 2025
e128481
feat: Match translations supported by Gutenberg Mobile
dcalhoun May 1, 2025
da26c1b
fix: Repair translations data path
dcalhoun May 1, 2025
6ab424e
fix: Start demo editor
dcalhoun May 1, 2025
c47453d
build: Add GUTERNBERG_EDITOR_REMOTE_URL environment value
dcalhoun May 1, 2025
edfd17f
fix: Repair script logger import path
dcalhoun May 1, 2025
6b78bf8
task: Reduce translation download script noise
dcalhoun May 1, 2025
971e831
feat: Control log levels for scripts
dcalhoun May 1, 2025
bf9614a
fix: Use default value from dynamic import
dcalhoun May 1, 2025
9f22787
refactor: Rename API fetch utility
dcalhoun May 1, 2025
e3d5a25
refactor: Rename exception parser utility
dcalhoun May 1, 2025
5a48230
fix: Postpone importing `@wordpress` packages until after setting locale
dcalhoun May 1, 2025
3ee0c86
refactor: Translate strings within `gutenberg-kit` domain
dcalhoun May 1, 2025
aa9bbf3
fix: Use existing Gutenberg "Block Settings" string
dcalhoun May 1, 2025
25dd3a0
fix: Apply `@wordpress` package import postponement to remote editor
dcalhoun May 2, 2025
913bd9f
refactor: Rename GBKit global await utility
dcalhoun May 2, 2025
158279d
fix: Repair localization operations order
dcalhoun May 2, 2025
d682d15
task: Log additional values
dcalhoun May 2, 2025
3f29a15
refactor: Separate concerns of loading assets and unregistering blocks
dcalhoun May 2, 2025
935c64f
refactor: Replace verbose log level with debug
dcalhoun May 2, 2025
948dc37
refactor: Replace redundant logger prefix with unique project prefix
dcalhoun May 2, 2025
ce3e042
task: Capture build output
dcalhoun May 2, 2025
df1ea42
build: Manually chunk editor utility
dcalhoun May 2, 2025
30097d5
fix: Avoid errors loading non-existent, default locale translations
dcalhoun May 2, 2025
9c0bdc3
task: Capture build output
dcalhoun May 2, 2025
26bcf2a
fix: Lazy load bundled `@wordpress` packages after locale is set
dcalhoun May 5, 2025
be3786b
fix: Ensure foundational styles are available for early error UI
dcalhoun May 5, 2025
f2682b8
refactor: Separate block utilities
dcalhoun May 6, 2025
930917b
refactor: `initializeEditor` provides default WP packages
dcalhoun May 6, 2025
310616c
build: Prevent unintentional preloading of `@wordpress` modules
dcalhoun May 8, 2025
0bb5fe6
task: Capture build output
dcalhoun May 8, 2025
7e22832
fix: Workaround circular dependency
dcalhoun May 8, 2025
1b3e577
task: Capture build output
dcalhoun May 8, 2025
a003792
refactor: Remove unused module
dcalhoun May 9, 2025
56ece8c
build: Cache translation strings
dcalhoun May 9, 2025
9e9f53b
refactor: Flatten translations directory
dcalhoun May 9, 2025
6a0df4a
refactor: Clarify translation script cache interaction
dcalhoun May 9, 2025
e812448
build: Add clean scripts
dcalhoun May 9, 2025
48fd3e7
task: Capture build output
dcalhoun May 9, 2025
c4e7789
refactor: Remove unused export
dcalhoun May 9, 2025
a74c229
fix: Organize dynamic imports and chunks to avoid circular dependencies
dcalhoun May 9, 2025
c391fea
refactor: Remove unnecessary dependency injection
dcalhoun May 9, 2025
e83c5f9
task: Capture build output
dcalhoun May 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,6 @@ local.properties
# /ios/Sources/GutenbergKit/Gutenberg/assets
# /ios/Sources/GutenbergKit/Gutenberg/index.html
# /ios/Sources/GutenbergKit/Gutenberg/remote.html

# Translation files
src/translations/
10 changes: 8 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@ define XCODEBUILD_CMD
endef

npm-dependencies:
@if [ "$(SKIP_DEPS)" != "true" ]; then \
@if [ "$(SKIP_DEPS)" != "true" ] && [ "$(SKIP_DEPS)" != "1" ]; then \
echo "--- :npm: Installing NPM Dependencies"; \
npm ci; \
fi

build: npm-dependencies
prep-translations:
@if [ "$(SKIP_L10N)" != "true" ] && [ "$(SKIP_L10N)" != "1" ]; then \
echo "--- :npm: Preparing Translations"; \
npm run prep-translations -- --force; \
fi

build: npm-dependencies prep-translations
echo "--- :node: Building Gutenberg"

npm run build
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ open class EditorConfiguration constructor(
val namespaceExcludedPaths: Array<String>,
val authHeader: String,
val webViewGlobals: List<WebViewGlobal>,
val editorSettings: String?
val editorSettings: String?,
val locale: String?
) : Parcelable {
companion object {
@JvmStatic
Expand All @@ -82,6 +83,7 @@ open class EditorConfiguration constructor(
private var authHeader: String = ""
private var webViewGlobals: List<WebViewGlobal> = emptyList()
private var editorSettings: String? = null
private var locale: String? = "en"

fun setTitle(title: String) = apply { this.title = title }
fun setContent(content: String) = apply { this.content = content }
Expand All @@ -97,6 +99,7 @@ open class EditorConfiguration constructor(
fun setAuthHeader(authHeader: String) = apply { this.authHeader = authHeader }
fun setWebViewGlobals(webViewGlobals: List<WebViewGlobal>) = apply { this.webViewGlobals = webViewGlobals }
fun setEditorSettings(editorSettings: String?) = apply { this.editorSettings = editorSettings }
fun setLocale(locale: String?) = apply { this.locale = locale }

fun build(): EditorConfiguration = EditorConfiguration(
title = title,
Expand All @@ -112,7 +115,8 @@ open class EditorConfiguration constructor(
namespaceExcludedPaths = namespaceExcludedPaths,
authHeader = authHeader,
webViewGlobals = webViewGlobals,
editorSettings = editorSettings
editorSettings = editorSettings,
locale = locale
)
}

Expand All @@ -136,6 +140,7 @@ open class EditorConfiguration constructor(
if (authHeader != other.authHeader) return false
if (webViewGlobals != other.webViewGlobals) return false
if (editorSettings != other.editorSettings) return false
if (locale != other.locale) return false

return true
}
Expand All @@ -155,6 +160,7 @@ open class EditorConfiguration constructor(
result = 31 * result + authHeader.hashCode()
result = 31 * result + webViewGlobals.hashCode()
result = 31 * result + (editorSettings?.hashCode() ?: 0)
result = 31 * result + (locale?.hashCode() ?: 0)
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ class GutenbergView : WebView {
"themeStyles": ${configuration.themeStyles},
"hideTitle": ${configuration.hideTitle},
"editorSettings": $editorSettings,
"locale": "${configuration.locale}",
${if (configuration.postId != null) """
"post": {
"id": ${configuration.postId},
Expand Down
158 changes: 158 additions & 0 deletions bin/prep-translations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/**
* External dependencies
*/
import fs from 'fs';
import path from 'path';
import fetch from 'node-fetch';

/**
* Internal dependencies
*/
import { info, error, debug } from '../src/utils/logger.js';

const TRANSLATIONS_DIR = path.join( process.cwd(), 'src/translations' );
const SUPPORTED_LOCALES = [
Copy link
Member Author

Choose a reason for hiding this comment

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

These are the same translations supported by Gutenberg Mobile.

'ar', // Arabic
'bg', // Bulgarian
'bo', // Tibetan
'ca', // Catalan
'cs', // Czech
'cy', // Welsh
'da', // Danish
'de', // German
'en-au', // English (Australia)
'en-ca', // English (Canada)
'en-gb', // English (UK)
'en-nz', // English (New Zealand)
'en-za', // English (South Africa)
'el', // Greek
'es', // Spanish
'es-ar', // Spanish (Argentina)
'es-cl', // Spanish (Chile)
'es-cr', // Spanish (Costa Rica)
'fa', // Persian
'fr', // French
'gl', // Galician
'he', // Hebrew
'hr', // Croatian
'hu', // Hungarian
'id', // Indonesian
'is', // Icelandic
'it', // Italian
'ja', // Japanese
'ka', // Georgian
'ko', // Korean
'nb', // Norwegian (Bokmål)
'nl', // Dutch
'nl-be', // Dutch (Belgium)
'pl', // Polish
'pt', // Portuguese
'pt-br', // Portuguese (Brazil)
'ro', // Romainian
'ru', // Russian
'sk', // Slovak
'sq', // Albanian
'sr', // Serbian
'sv', // Swedish
'th', // Thai
'tr', // Turkish
'uk', // Ukrainian
'ur', // Urdu
'vi', // Vietnamese
'zh-cn', // Chinese (China)
'zh-tw', // Chinese (Taiwan)
];

/**
* Prepare translations for all supported locales.
*
* @param {boolean} force Whether to force download even if cache exists.
*
* @return {Promise<void>} A promise that resolves when translations are prepared.
*/
async function prepareTranslations( force = false ) {
if ( force ) {
info( 'Ignoring cache, downloading translations...' );
} else {
info( 'Verifying translations...' );
}

for ( const locale of SUPPORTED_LOCALES ) {
try {
await downloadTranslations( locale, force );
} catch ( err ) {
error( `✗ Failed to download translations for ${ locale }:`, err );
}
}

info( '✓ Translations ready!' );
}

/**
* Downloads translations for a specific locale from translate.wordpress.org.
*
* @param {string} locale The locale to download translations for.
* @param {boolean} force Whether to force download even if cache exists.
*
* @return {Promise<void>} A promise that resolves when translations are downloaded.
*/
async function downloadTranslations( locale, force = false ) {
if ( ! force && hasValidTranslations( locale ) ) {
debug( `Skipping download of cached translations for ${ locale }` );
return;
}
debug( `Downloading translations for ${ locale }...` );

const url = `https://translate.wordpress.org/projects/wp-plugins/gutenberg/dev/${ locale }/default/export-translations/?format=json`;
const response = await fetch( url );

if ( ! response.ok ) {
throw new Error( `Failed to download translations for ${ locale }` );
}

const translations = await response.json();
const outputPath = path.join( TRANSLATIONS_DIR, `${ locale }.json` );

// Ensure the translations directory exists
if ( ! fs.existsSync( TRANSLATIONS_DIR ) ) {
fs.mkdirSync( TRANSLATIONS_DIR, { recursive: true } );
}

// Write translations to file
fs.writeFileSync( outputPath, JSON.stringify( translations, null, 2 ) );
debug( `✓ Downloaded translations for ${ locale }` );
}

/**
* Checks if translations exist and are valid for a specific locale.
*
* @param {string} locale The locale to check.
*
* @return {boolean} Whether valid translations exist.
*/
function hasValidTranslations( locale ) {
const filePath = path.join( TRANSLATIONS_DIR, `${ locale }.json` );
if ( ! fs.existsSync( filePath ) ) {
return false;
}

try {
const content = fs.readFileSync( filePath, 'utf8' );
const translations = JSON.parse( content );
return translations && typeof translations === 'object';
} catch ( err ) {
return false;
}
}

/**
* Main entry point for the script.
* Parses command line arguments and downloads translations.
*/
const forceDownload =
process.argv.includes( '--force' ) || process.argv.includes( '-f' );

prepareTranslations( forceDownload ).catch( ( err ) => {
error( 'Failed to prepare translations:', err );
process.exit( 1 );
} );
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@
value = "http://localhost:5173/"
isEnabled = "NO">
</EnvironmentVariable>
<EnvironmentVariable
key = "GUTENBERG_EDITOR_REMOTE_URL"
value = "http://localhost:5174/remote.html"
isEnabled = "NO">
</EnvironmentVariable>
Comment on lines +59 to +63
Copy link
Member Author

Choose a reason for hiding this comment

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

Disabled by default. Added to simplify testing the remote editor.

</EnvironmentVariables>
</LaunchAction>
<ProfileAction
Expand Down
1 change: 1 addition & 0 deletions ios/Demo-iOS/Sources/EditorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ private struct _EditorView: UIViewControllerRepresentable {
if #available(iOS 16.4, *) {
viewController.webView.isInspectable = true
}
viewController.startEditorSetup()
Copy link
Member Author

Choose a reason for hiding this comment

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

Adding this required start call to the demo app was overlooked during #114.

return viewController
}

Expand Down
12 changes: 12 additions & 0 deletions ios/Sources/GutenbergKit/Gutenberg/assets/ar-CyYCT0Yn.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions ios/Sources/GutenbergKit/Gutenberg/assets/bg-B2djTlFt.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions ios/Sources/GutenbergKit/Gutenberg/assets/bo-DOe3GxAO.js

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions ios/Sources/GutenbergKit/Gutenberg/assets/bridge-CYSxxzEK.js

This file was deleted.

12 changes: 12 additions & 0 deletions ios/Sources/GutenbergKit/Gutenberg/assets/ca--D3R8toT.js

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions ios/Sources/GutenbergKit/Gutenberg/assets/cs-DWmcEIfO.js

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions ios/Sources/GutenbergKit/Gutenberg/assets/cy-CGIlBSk6.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions ios/Sources/GutenbergKit/Gutenberg/assets/da-DwB1fLza.js

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions ios/Sources/GutenbergKit/Gutenberg/assets/de-H01QIQZg.js

Large diffs are not rendered by default.

Loading