Possible Solution to Capacitor and Angular i18n incompatibility

We used the built-in Angular Internationalization for our ionic app. When trying to build the native ran into we the following issue:
The web directory (/path/to/project/www) must contain an "index.html". This is due to the fact that the index.html file is located in the respective language subfolder (e.g. www/en-US) after the angular localize feature did it’s job.

It is often suggested to use the NGX Translate package instead, as it is able to switch dynamically between languages inside the app instead of building two separate versions of the app. We preferred to use the Angular i18n over the NGX Translate package since it’s the officially supported approach that is guaranteed to work with new angular versions. Especially because Ngx translate was abandoned for a long time and only recently got revived.

Below is our current solution/workaround. We would appreciate any feedback. Do you see any issues with our approach?

We managed to build the native app with both English and German languages packaged in the same APK.

The ionic build:after hook allows to execute custom javascript as part of the capacitor build process. The code is executed after the angular build and therefore after the localization.
We wrote a small js script, that creates a custom index.html file in the root of the www directory, which redirects to the correct language-specific version of the app within the corresponding folder (in our case either “de” or “en-GB”).

We added the following to the ionic.config.json:

  "hooks": {
    "build:after": "src/scripts/after-angular-build-hook.js"
  }

The contents of the “after-angular-build-hook.js” are as follows (for “de” and “en-GB” locales):

// File needs to be CommonJs
const fs = require("fs");

const INDEX_HTML_PATH = "./www/index.html";
const htmlContent =
  '<script type="text/javascript">const currentLocale = navigator.language; if (currentLocale === "de") { window.location.href = "./de/index.html"; } else { window.location.href = "./en-GB/index.html"; } </script>';

module.exports = function () {
  console.log("creating index.html...");
  createIndexHtml();
};

function createIndexHtml() {
  fs.writeFile(INDEX_HTML_PATH, htmlContent, (error) => {
    if (error) {
      throw new Error(`Error writing file: ${INDEX_HTML_PATH}`, { cause: error });
    } else {
      console.log("Successfully created file:", INDEX_HTML_PATH);
    }
  });
}

In order to ensure the Android app is showing the correct language based on the current system language, we needed to add some code to the MainActivity in the Android source folder of our ionic project.

public class MainActivity extends BridgeActivity {
  private static final String LOCALE_DE = "de";
  private static final String LOCALE_EN_GB = "en-GB";

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setLocaleFromDeviceLanguage();
  }

  private void setLocaleFromDeviceLanguage() {
    Locale deviceLocale;
    // The getResources().getConfiguration().locale method has been deprecated in Android N (API level 24).
    // It's better to use getResources().getConfiguration().getLocales().get(0) to get the current locale.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
      deviceLocale = getResources().getConfiguration().getLocales().get(0);
    } else {
      deviceLocale = getResources().getConfiguration().locale;
    }

    // Set the app's locale based on the detected language
    if (deviceLocale.getLanguage().equals(LOCALE_DE)) {
      setLocale(LOCALE_DE);
    } else {
      setLocale(LOCALE_EN_GB);
    }
  }

  private void setLocale(String localeCode) {
    Locale locale = new Locale(localeCode);
    Locale.setDefault(locale);

    Configuration config = new Configuration();
    config.locale = locale;

    getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
  }
}

Build the app e.g. with the ionic capacitor build android command should now succeed. When running as a native app with ionic cap run android switching the system language of the Android phone should now lead to the app being loaded in that language.

1 Like

Hello, I am trying to implement this solution in my project, I have a question how do you build your locales before building it in android, in my case android build angular app first and copy the files in the public folder in angular project, I don’t know how to be able to build multiple locales and copy them to android project!!!

Hello, sorry for the late response. We did not configure anything special. Building multiple locales is handled by the Angular Internationalization. Make sure that your angular.json file is configured properly to produce the 2 locales in the www folder. You can also refer to the official angular guide: Angular
Our angular.json looks like this:

"architect": {
  "build": {
    "builder": "@angular-devkit/build-angular:browser",
    "options": {
      "localize": true,
      "outputPath": "www",
      "index": "src/index.html",
      [...]
    }
    "configurations": {
      [...]
        "de": {
          "localize": ["de"]
        },
        "en-GB": {
          "localize": ["en-GB"]
        },
      [...]
      }
  }

With this configured correctly, you can see that Localized bundle generation is done before copy android when running ionic capacitor build android:

$ ionic capacitor build android
> ng.cmd run app:build
✔ Browser application bundle generation complete.
✔ Localized bundle generation complete.
✔ Copying assets complete.
⠋ Generating index html...
[...]
creating index.html...
Successfully created file: ./www/index.html
[...]
> capacitor.cmd sync android
[capacitor] √ copy android in 5.58s

Configure your angular.json: Set the “localize” option to true or to an array of locale IDs. This tells the Angular CLI to build versions for each locale specified. Specify the “baseHref” for each locale: In your angular.json, set the “baseHref” for each locale so that the CLI can adjust the base href for each version of the application. ProCADIS budget-friendly software choices. Build your project: Use the Angular CLI to build your project. If you have configured everything correctly in angular.json, it should create separate directories for each locale, such as myapp/fr for French and myapp/es for Spanish.
Copy the files to your Android project: After building your Angular app with multiple locales, you can copy the output from these directories to the public folder in your Android project.