Skip to content

Commit 25dbe3e

Browse files
shai-almogclaude
andauthored
Fix initializr localization bundle path (NPE in MyAppName.init) (#4852)
* Fix initializr localization bundles + harden simulator designer lookup The initializr's "include localization bundles" option generated bundles under common/src/main/resources/messages*.properties, but the CN1 maven plugin's CSS compiler scans common/src/main/l10n (or i18n) for bundles to bake into theme.res. The result: Resources.getGlobalResources().getL10N( "messages", lang) hit a missing resource id at simulator startup and threw NPE in MyAppName.init -- the project couldn't run. - GeneratorModel.addLocalizationEntries: write to src/main/l10n so the bundles actually end up inside theme.res. - Bootstrap (Java + Kotlin) injected into the starter class is now null-safe and falls back to the default locale when the device language has no specific bundle. - Resources.getL10N / listL10NLocales / l10NLocaleSet now return null instead of NPE-ing when the bundle id is absent. Defensive change at the framework level so any project shipping mismatched bundles degrades gracefully. - New tests/core/.../ResourcesL10NTest covers the framework null-safety. - GeneratorModelMatrixTest now asserts bundles land under l10n and are NOT under src/main/resources (catches the regression at unit-test time). - GeneratorModelIntegrationBuildTest now opens common/target/classes/ theme.res after mvn compile and verifies "messages" L10N data is present for both the default ("") and Hebrew ("he") locales -- the end-to-end signal the previous tests missed. While here, harden the simulator's CSS compiler invocation against stale ~/.codenameone/designer_1.jar: - New MavenUtils.findDesignerJarInM2 derives the running CN1 version from the codenameone-core jar's m2 path and resolves the matching codenameone-designer-<version>-jar-with-dependencies.jar. Any plugin invocation has already pulled this into m2 as a plugin dependency. - CSSWatcher and ComponentTreeInspector now prefer codename1.designer.jar -> m2 designer -> ~/.codenameone fallback (with a clear warning when the legacy fallback is hit). The build-time CSS goal already used getDesignerJar() so this only affects the simulator runtime / live CSS reload paths. - CompileCSSMojo now invokes the forked CSS compiler with INFO log level instead of DEBUG so subprocess stack traces are visible without re-running with -X. This won't fix issue #4850 but makes the next similar report actionable. - The four initializr pom templates replace skipexisting="true" with usetimestamp="true" on the UpdateCodenameOne.jar download so future installs refresh the updater jar instead of pinning forever to the first copy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * CompileCSSMojo: route INFO log level through createJava() override The previous patch raised the CSS subprocess log level by passing it explicitly at the call site (createJava(LEVEL_INFO)), which bypasses CompileCSSMojoTest's TestCompileCSSMojo.createJava() override -- the test substitutes a RecordingJava there to capture the command line without forking. The override was no longer hit, so the test fell through to a real fork against a stub designer.jar and four tests errored out with "Invalid or corrupt jarfile". Move the INFO log level into a createJava() override on CompileCSSMojo itself. The call site stays at createJava(), so the test override continues to win, and production still gets INFO so subprocess stack traces remain visible without -X. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix CSS compiler StringIndexOutOfBoundsException for initializr projects The user shared the missing stack trace from issue #4850: at java.base/java.lang.String.substring(String.java:2899) at com.codename1.ui.plaf.UIManager.parseTextFieldInputMode(UIManager.java:2434) at com.codename1.ui.plaf.UIManager.setBundle(UIManager.java:2419) at com.codename1.impl.javase.JavaSEPort.enableAutoLocalizationBundle(...) at com.codename1.impl.javase.JavaSEPort.init(JavaSEPort.java:5598) at com.codename1.impl.CodenameOneImplementation.initImpl(...) at com.codename1.ui.Display.init(Display.java:351) at com.codename1.designer.css.CN1CSSCLI.main(CN1CSSCLI.java:713) This is not a path-related bug -- every initializr-generated project hits it at css-goal time. Three pieces interact: 1. JavaSEPort.findLocalizationDirectory auto-creates src/main/l10n if it is missing, and enableAutoLocalizationBundle installs an AutoLocalizationBundle for it. 2. AutoLocalizationBundle.get echoes any missing key back as its own value -- the simulator's "wormhole" so devs can spot untranslated strings. 3. UIManager.setBundle queries "@im" on every bundle install. With the echo behavior, "@im" -> "@im", which is then tokenized to ["@im"], "@im-@im" is queried (which echoes "@im-@im"), and parseTextFieldInputMode crashes on substring(0, indexOf('=')) because that token has no '=' (range [0, -1) of length 7). The CSS compiler subprocess inherits all of this because CN1CSSCLI.main calls Display.init -> JavaSEPort.init -> enableAutoLocalizationBundle. Fixes: - AutoLocalizationBundle.get returns null for keys starting with '@'. Meta-keys (@rtl, @im, @im-<name>) are configuration entries that callers distinguish from "missing" by checking for null. Echoing the key back is semantically wrong AND broke setBundle. Real meta-key values that exist in the underlying file (e.g. @rtl=true in a Hebrew bundle) are still returned -- only fabrication is suppressed. - UIManager.parseTextFieldInputMode skips tokens without '=' and skips entries whose key isn't a valid integer. Defensive belt-and-suspenders so any bundle with malformed input-mode entries degrades gracefully instead of failing the whole bundle install. - New UIManagerSetBundleTest exercises setBundle against an echo-bundle (matches pre-fix AutoLocalizationBundle behavior) and against directly malformed @im/@im-x entries. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Revert parseTextFieldInputMode fail-safe; keep AutoLocalizationBundle fix Per review: silently skipping malformed `@im` tokens hides legitimate bugs in user-supplied bundles. Real malformed input should fail loudly, not be swallowed. The actual root cause -- AutoLocalizationBundle fabricating values for @-prefixed meta-keys -- stays fixed. That's the surgical change: the auto-localize wormhole was returning fake values for keys that callers explicitly use null/non-null to gate features (@im, @rtl, @im-<name>), which is semantically wrong and broke setBundle. Moves the regression coverage from the no-op stub in core to the existing AutoLocalizationBundleTest in the JavaSE port (where the bundle class actually lives), asserting: - @-prefixed meta-keys are NOT auto-fabricated - @-prefixed meta-keys that exist in the underlying file ARE returned Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Initializr: ship Bundle.properties stub with @im= to dodge simulator wormhole Workaround for the AutoLocalizationBundle @im fabrication crash in shipped Codename One <= 7.0.236. The proper fix lives in JavaSEPort (don't fabricate values for `@`-prefixed meta-keys) and is on this branch, but it requires a new framework release. Until then, every initializr-generated project crashes at css-goal time inside the CSS compiler subprocess (CN1CSSCLI -> Display.init -> JavaSEPort.init -> enableAutoLocalizationBundle -> UIManager.setBundle -> parseTextFieldInputMode on substring(0, -1) for "@im-@im"). Ship `common/src/main/l10n/Bundle.properties` with a single `@im=` entry on every generated project. Two reasons it works: 1. JavaSEPort.findDefaultLocalizationBundleFile prefers Bundle.properties over any other file in src/main/l10n, so the AutoLocalizationBundle loads our stub as its base. 2. With `@im=""` already in the bundle's underlying Hashtable, AutoLocalizationBundle.get("@im") returns "" instead of fabricating "@im". setBundle sees length 0 and skips the input-mode block, so parseTextFieldInputMode is never called. The stub is unconditional (added to every project, with or without localization bundles enabled) because the bug fires regardless -- enableAutoLocalizationBundle auto-creates src/main/l10n in the CSS compiler subprocess even on projects that didn't request localization. The matrix test asserts the stub is present on every generated project combination so this workaround can't silently regress. Once the AutoLocalizationBundle fix lands in a release and the initializr is bumped past it, this stub can be removed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 72e4758 commit 25dbe3e

15 files changed

Lines changed: 313 additions & 34 deletions

File tree

CodenameOne/src/com/codename1/ui/util/Resources.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -929,7 +929,11 @@ InputStream getUi(String id) {
929929
///
930930
/// Hashtable containing key value pairs for localized data
931931
public Hashtable<String, String> getL10N(String id, String locale) {
932-
return (Hashtable<String, String>) ((Hashtable) resources.get(id)).get(locale);
932+
Hashtable bundles = (Hashtable) resources.get(id);
933+
if (bundles == null) {
934+
return null;
935+
}
936+
return (Hashtable<String, String>) bundles.get(locale);
933937
}
934938

935939
/// Returns an enumration of the locales supported by this resource id
@@ -942,7 +946,11 @@ public Hashtable<String, String> getL10N(String id, String locale) {
942946
///
943947
/// enumeration of strings containing bundle names
944948
public Enumeration listL10NLocales(String id) {
945-
return ((Hashtable) resources.get(id)).keys();
949+
Hashtable bundles = (Hashtable) resources.get(id);
950+
if (bundles == null) {
951+
return null;
952+
}
953+
return bundles.keys();
946954
}
947955

948956
/// Returns a collection of the l10 locale names
@@ -955,7 +963,11 @@ public Enumeration listL10NLocales(String id) {
955963
///
956964
/// collection of strings containing bundle names
957965
public Collection<String> l10NLocaleSet(String id) {
958-
return ((Hashtable<String, String>) resources.get(id)).keySet();
966+
Hashtable<String, String> bundles = (Hashtable<String, String>) resources.get(id);
967+
if (bundles == null) {
968+
return null;
969+
}
970+
return bundles.keySet();
959971
}
960972

961973
/// Returns the font resource from the file

Ports/JavaSE/src/com/codename1/impl/javase/CSSWatcher.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,22 @@ public void run() {
272272
File designerJar = new File(cn1Home, "designer_1.jar");
273273
if (System.getProperty("codename1.designer.jar", null) != null) {
274274
designerJar = new File(System.getProperty("codename1.designer.jar", null));
275+
} else {
276+
// The Maven plugin declares codenameone-designer as a plugin dependency, so any
277+
// CN1 mojo invocation has already pulled the version-pinned designer jar into m2.
278+
// Prefer that over ~/.codenameone/designer_1.jar (which is managed by UpdateCodenameOne
279+
// and routinely lags behind the plugin version, producing confusing CSS failures).
280+
File m2Designer = com.codename1.impl.javase.util.MavenUtils.findDesignerJarInM2();
281+
if (m2Designer != null) {
282+
designerJar = m2Designer;
283+
} else if (designerJar.exists()) {
284+
System.err.println("[CSSWatcher] Warning: codename1.designer.jar system property is not set "
285+
+ "and no version-pinned designer was found in the local Maven repository; "
286+
+ "falling back to " + designerJar.getAbsolutePath() + ". This file is "
287+
+ "managed by UpdateCodenameOne and may be older than the CN1 plugin in use. "
288+
+ "If CSS compilation fails, launch the simulator via the Maven cn1:run goal "
289+
+ "(which both fetches the right designer into m2 and pins it via -Dcodename1.designer.jar).");
290+
}
275291
}
276292
String cefDir = System.getProperty("cef.dir", cn1Home + File.separator + "cef");
277293

Ports/JavaSE/src/com/codename1/impl/javase/ComponentTreeInspector.java

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -580,18 +580,32 @@ private void findScrollableContainers(Container cnt, List<Container> response) {
580580
}
581581

582582
private void editStyle() {
583-
File cn1dir = new File(System.getProperty("user.home"), ".codenameone");
584-
if(!cn1dir.exists()) {
585-
JOptionPane.showMessageDialog(this, "Please open the designer once by opening the theme.res file", "Error Opening Designer", JOptionPane.ERROR_MESSAGE);
586-
return;
583+
// Prefer the version-pinned designer jar that the Maven plugin pulled into m2.
584+
// Fallback to the legacy ~/.codenameone/designer_*.jar files (managed by UpdateCodenameOne).
585+
File resourceEditor = null;
586+
if (System.getProperty("codename1.designer.jar", null) != null) {
587+
resourceEditor = new File(System.getProperty("codename1.designer.jar"));
587588
}
588-
File resourceEditor = new File(cn1dir, "designer_1.jar");
589-
if(!resourceEditor.exists()) {
590-
resourceEditor = new File(cn1dir, "designer.jar");
589+
if (resourceEditor == null || !resourceEditor.exists()) {
590+
File m2Designer = com.codename1.impl.javase.util.MavenUtils.findDesignerJarInM2();
591+
if (m2Designer != null) {
592+
resourceEditor = m2Designer;
593+
}
591594
}
592-
if(!resourceEditor.exists()) {
593-
JOptionPane.showMessageDialog(this, "Please open the designer once by opening the theme.res file", "Error Opening Designer", JOptionPane.ERROR_MESSAGE);
594-
return;
595+
if (resourceEditor == null || !resourceEditor.exists()) {
596+
File cn1dir = new File(System.getProperty("user.home"), ".codenameone");
597+
if(!cn1dir.exists()) {
598+
JOptionPane.showMessageDialog(this, "Please open the designer once by opening the theme.res file", "Error Opening Designer", JOptionPane.ERROR_MESSAGE);
599+
return;
600+
}
601+
resourceEditor = new File(cn1dir, "designer_1.jar");
602+
if(!resourceEditor.exists()) {
603+
resourceEditor = new File(cn1dir, "designer.jar");
604+
}
605+
if(!resourceEditor.exists()) {
606+
JOptionPane.showMessageDialog(this, "Please open the designer once by opening the theme.res file", "Error Opening Designer", JOptionPane.ERROR_MESSAGE);
607+
return;
608+
}
595609
}
596610

597611
File javaBin = new File(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java.exe");

Ports/JavaSE/src/com/codename1/impl/javase/JavaSEPort.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14295,6 +14295,15 @@ public synchronized String get(Object key) {
1429514295
if (key instanceof String) {
1429614296
String strKey = (String) key;
1429714297
if (value == null) {
14298+
// Don't auto-fabricate values for meta-keys like @rtl, @im, @im-<name>.
14299+
// These are configuration entries that callers (e.g. UIManager.setBundle)
14300+
// distinguish from "missing" by checking for null. If we echo the key back
14301+
// as the value, setBundle will treat "@im" as a real input-mode descriptor,
14302+
// tokenize it, and crash inside parseTextFieldInputMode when the resulting
14303+
// token has no '='.
14304+
if (strKey.startsWith("@")) {
14305+
return null;
14306+
}
1429814307
String autoValue = strKey;
1429914308
putInternal(strKey, autoValue);
1430014309
storeEntry(strKey, autoValue, true);

Ports/JavaSE/src/com/codename1/impl/javase/util/MavenUtils.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
package com.codename1.impl.javase.util;
77

88
import com.codename1.io.Log;
9+
import com.codename1.ui.Display;
910
import java.io.File;
11+
import java.net.URL;
1012

1113
/**
1214
*
@@ -63,6 +65,49 @@ public static File findJavac() {
6365
return null;
6466
}
6567

68+
/**
69+
* Locate the codenameone-designer:jar-with-dependencies jar inside the local
70+
* Maven (~/.m2) repository, using the version of the codenameone-core jar that
71+
* is currently loaded into this JVM. Returns null if the running framework is
72+
* not loaded from m2 (e.g. running from a build directory) or if the matching
73+
* designer jar has not been resolved yet.
74+
*
75+
* <p>The Maven plugin declares codenameone-designer as a plugin dependency, so
76+
* any plugin invocation (cn1:run, mvn compile when bound to the css goal, etc.)
77+
* implicitly fetches the matching designer jar into m2. This lookup lets the
78+
* simulator runtime use that exact version even when codename1.designer.jar
79+
* isn't passed as a system property -- avoiding a stale ~/.codenameone/designer_1.jar
80+
* fallback.
81+
*/
82+
public static File findDesignerJarInM2() {
83+
try {
84+
URL location = Display.class.getProtectionDomain().getCodeSource().getLocation();
85+
if (location == null) {
86+
return null;
87+
}
88+
File coreJar = new File(location.toURI());
89+
// Expected layout: <repo>/com/codenameone/codenameone-core/<version>/codenameone-core-<version>.jar
90+
File versionDir = coreJar.getParentFile();
91+
if (versionDir == null) return null;
92+
File coreDir = versionDir.getParentFile();
93+
if (coreDir == null) return null;
94+
File codenameoneGroupDir = coreDir.getParentFile();
95+
if (codenameoneGroupDir == null) return null;
96+
if (!"codenameone-core".equals(coreDir.getName())) {
97+
return null;
98+
}
99+
String version = versionDir.getName();
100+
File designerVersionDir = new File(codenameoneGroupDir, "codenameone-designer" + File.separator + version);
101+
File designer = new File(designerVersionDir, "codenameone-designer-" + version + "-jar-with-dependencies.jar");
102+
if (designer.isFile()) {
103+
return designer;
104+
}
105+
} catch (Throwable t) {
106+
// Best-effort lookup. Any unexpected layout means we can't resolve via m2.
107+
}
108+
return null;
109+
}
110+
66111
public static boolean isRunningInJDK() {
67112
if (!isRunningInJDKChecked) {
68113
isRunningInJDKChecked = true;

maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/CompileCSSMojo.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,27 @@
3838
*
3939
* @author shannah
4040
*/
41-
@Mojo(name = "css", defaultPhase = LifecyclePhase.PROCESS_RESOURCES,
41+
@Mojo(name = "css", defaultPhase = LifecyclePhase.PROCESS_RESOURCES,
4242
requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME,
4343
requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME)
4444
public class CompileCSSMojo extends AbstractCN1Mojo {
4545

46+
/**
47+
* Override the default DEBUG log level so the forked CSS compiler's stdout
48+
* is visible in normal mvn output. When the CSS subprocess throws (e.g.
49+
* StringIndexOutOfBoundsException in CN1CSSCLI), users currently only see
50+
* the wrapper "An error occurred while compiling the CSS files" message
51+
* with no usable detail unless they re-run with -X.
52+
*
53+
* Routed through createJava() (not the call site) so subclasses that
54+
* override createJava() in tests still get to substitute their recording
55+
* Java task without having to know about the log level.
56+
*/
57+
@Override
58+
public org.apache.tools.ant.taskdefs.Java createJava() {
59+
return createJava(org.apache.maven.doxia.logging.Log.LEVEL_INFO);
60+
}
61+
4662

4763
@Override
4864
protected void executeImpl() throws MojoExecutionException, MojoFailureException {
@@ -232,11 +248,14 @@ private void executeImpl(String themePrefix) throws MojoExecutionException, Mojo
232248

233249

234250

235-
// Run the CSS compiler which is contained inside the codenameone-designer jar
251+
// Run the CSS compiler which is contained inside the codenameone-designer jar.
236252
// NOTE: The codenameone-designer.jar is a dependency of the codenameone-maven-plugin as
237253
// zip file (which is the designer jar with all dependencies). We use this jar
238254
// rather than the central designer_1.jar located in the user's home directory to make it
239255
// easier to pin to a particular version.
256+
// The Java task is created via createJava() (overridden in this class to use INFO log
257+
// level) so subprocess output -- including stack traces from CN1CSSCLI failures --
258+
// shows up in normal mvn output instead of being hidden at DEBUG.
240259
Java java = createJava();
241260
java.setDir(getCN1ProjectDir());
242261
java.setJar(getDesignerJar());

scripts/initializr/common/src/main/java/com/codename1/initializr/model/GeneratorModel.java

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ void writeProjectZip(OutputStream outputStream) throws IOException {
9090
copyZipEntriesToMap(template.CSS, mergedEntries, ZipEntryType.TEMPLATE_CSS);
9191
copyZipEntriesToMap(template.SOURCE_ZIP, mergedEntries, ZipEntryType.TEMPLATE_SOURCE);
9292
addLocalizationEntries(mergedEntries);
93+
addAutoLocalizationBundleStub(mergedEntries);
9394

9495
try (ZipOutputStream zos = new ZipOutputStream(outputStream)) {
9596
for (Map.Entry<String, byte[]> fileEntry : mergedEntries.entrySet()) {
@@ -102,12 +103,53 @@ void writeProjectZip(OutputStream outputStream) throws IOException {
102103
}
103104

104105

106+
/**
107+
* Workaround for a bug in shipped Codename One versions (<= 7.0.236) where the
108+
* simulator's AutoLocalizationBundle echoes any missing key back as its own value.
109+
* UIManager.setBundle queries `@im` on every bundle install, gets `"@im"` back from
110+
* the wormhole, tokenizes it, queries `"@im-@im"`, gets `"@im-@im"` back, then
111+
* crashes inside parseTextFieldInputMode on substring(0, indexOf('=')) for a token
112+
* with no `=`. The CSS compiler subprocess (CN1CSSCLI -> Display.init -> JavaSEPort.init
113+
* -> enableAutoLocalizationBundle) hits this on every initializr-generated project.
114+
*
115+
* The proper fix lives in JavaSEPort.AutoLocalizationBundle (don't fabricate values
116+
* for `@`-prefixed meta-keys), but that requires a new framework release. As a
117+
* workaround we ship an empty `Bundle.properties` with `@im=`, which:
118+
* 1. Is preferred by JavaSEPort.findDefaultLocalizationBundleFile over any other
119+
* bundle file in src/main/l10n, so the AutoLocalizationBundle loads it as base.
120+
* 2. Pre-populates `@im=""` in the bundle's underlying Hashtable, so
121+
* AutoLocalizationBundle.get("@im") returns "" (not the fabricated "@im"),
122+
* which has length 0, so setBundle skips the input-mode block entirely.
123+
*
124+
* This is unconditional (added to every generated project) because
125+
* enableAutoLocalizationBundle auto-creates `src/main/l10n` even when the user
126+
* didn't ask for localization bundles, so the crash hits projects without any
127+
* localization too. Remove this stub once the framework fix has shipped and
128+
* cn1.plugin.version is bumped past it.
129+
*/
130+
private void addAutoLocalizationBundleStub(Map<String, byte[]> mergedEntries) throws IOException {
131+
String stub = "# Workaround for the simulator AutoLocalizationBundle @im fabrication crash\n"
132+
+ "# in Codename One <= 7.0.236. Once the framework fix ships, this file can be removed.\n"
133+
+ "# See GeneratorModel.addAutoLocalizationBundleStub for the full story.\n"
134+
+ "@im=\n";
135+
copySingleTextEntryToMap(
136+
"common/src/main/l10n/Bundle.properties",
137+
stub,
138+
mergedEntries,
139+
ZipEntryType.COMMON
140+
);
141+
}
142+
105143
private void addLocalizationEntries(Map<String, byte[]> mergedEntries) throws IOException {
106144
if (!isBareTemplate() || !options.includeLocalizationBundles) {
107145
return;
108146
}
147+
// The Codename One Maven plugin's CSS compiler scans src/main/l10n (or src/main/i18n)
148+
// for *.properties bundles and bakes them into theme.res. If the bundles are placed
149+
// anywhere else (e.g. src/main/resources) they are NOT baked into the resource file
150+
// and Resources.getGlobalResources().getL10N("messages", lang) returns null at runtime.
109151
copySingleTextEntryToMap(
110-
"common/src/main/resources/messages.properties",
152+
"common/src/main/l10n/messages.properties",
111153
readResourceToString("/messages.properties"),
112154
mergedEntries,
113155
ZipEntryType.COMMON
@@ -117,7 +159,7 @@ private void addLocalizationEntries(Map<String, byte[]> mergedEntries) throws IO
117159
continue;
118160
}
119161
copySingleTextEntryToMap(
120-
"common/src/main/resources/messages_" + language.bundleSuffix + ".properties",
162+
"common/src/main/l10n/messages_" + language.bundleSuffix + ".properties",
121163
readResourceToString("/messages_" + language.bundleSuffix + ".properties"),
122164
mergedEntries,
123165
ZipEntryType.COMMON
@@ -356,8 +398,14 @@ private String injectJavaLocalizationBootstrap(String content) {
356398
+ " public void init(Object context) {\n"
357399
+ " super.init(context);\n"
358400
+ " String language = L10NManager.getInstance().getLanguage();\n"
359-
+ " Hashtable<String, String> bundle = Resources.getGlobalResources().getL10N(\"messages\", language);\n"
360-
+ " UIManager.getInstance().setBundle(bundle);\n"
401+
+ " Resources global = Resources.getGlobalResources();\n"
402+
+ " Hashtable<String, String> bundle = global == null ? null : global.getL10N(\"messages\", language);\n"
403+
+ " if (bundle == null && global != null) {\n"
404+
+ " bundle = global.getL10N(\"messages\", \"\");\n"
405+
+ " }\n"
406+
+ " if (bundle != null) {\n"
407+
+ " UIManager.getInstance().setBundle(bundle);\n"
408+
+ " }\n"
361409
+ " }\n\n";
362410
int firstBrace = content.indexOf('{');
363411
if (firstBrace > -1) {
@@ -374,8 +422,14 @@ private String injectKotlinLocalizationBootstrap(String content) {
374422
String method = "\n override fun init(context: Any?) {\n"
375423
+ " super.init(context)\n"
376424
+ " val language = L10NManager.getInstance().language\n"
377-
+ " val bundle: Hashtable<String, String>? = Resources.getGlobalResources().getL10N(\"messages\", language)\n"
378-
+ " UIManager.getInstance().setBundle(bundle)\n"
425+
+ " val global = Resources.getGlobalResources()\n"
426+
+ " var bundle: Hashtable<String, String>? = global?.getL10N(\"messages\", language)\n"
427+
+ " if (bundle == null) {\n"
428+
+ " bundle = global?.getL10N(\"messages\", \"\")\n"
429+
+ " }\n"
430+
+ " if (bundle != null) {\n"
431+
+ " UIManager.getInstance().setBundle(bundle)\n"
432+
+ " }\n"
379433
+ " }\n\n";
380434
int firstBrace = content.indexOf('{');
381435
if (firstBrace > -1) {

scripts/initializr/common/src/main/resources/barebones-pom.xml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,18 @@
5353

5454
<mkdir dir="${user.home}/.codenameone"/>
5555
<mkdir dir="${project.build.directory}/codenameone/tmpProject"/>
56+
<!-- usetimestamp re-downloads UpdateCodenameOne.jar when the
57+
remote is newer than the local copy; skipexisting="true"
58+
pinned users to whatever stale copy was first downloaded
59+
and led to outdated guibuilder/designer jars. -->
5660
<get src="https://www.codenameone.com/files/updates/UpdateCodenameOne.jar"
5761
dest="${user.home}/UpdateCodenameOne.jar"
58-
skipexisting="true"
62+
usetimestamp="true"
5963
ignoreerrors="true"/>
6064

6165
<get src="https://github.com/codenameone/CodenameOne/raw/refs/heads/master/maven/UpdateCodenameOne.jar"
6266
dest="${user.home}/UpdateCodenameOne.jar"
63-
skipexisting="true"
67+
usetimestamp="true"
6468
ignoreerrors="false"/>
6569
<java jar="${user.home}/UpdateCodenameOne.jar" fork="true">
6670
<arg value="${project.build.directory}/codenameone/tmpProject"/>

0 commit comments

Comments
 (0)