Internationalization changed in Ionic V7?

I have two ionic vue apps… one upgraded from V4 and one started over on v7
both show the same info

Mac-mini:xxx sam$ ionic --version
7.2.0
Mac-mini:xxx sam$ npx cap doctor
[warn] The bundledWebRuntime configuration option has been deprecated. Can be safely deleted.
💊   Capacitor Doctor  💊 

Latest Dependencies:

  @capacitor/cli: 6.0.0
  @capacitor/core: 6.0.0
  @capacitor/android: 6.0.0
  @capacitor/ios: 6.0.0

Installed Dependencies:

  @capacitor/cli: 6.0.0
  @capacitor/android: 6.0.0
  @capacitor/core: 6.0.0
  @capacitor/ios: 6.0.0

[success] iOS looking great! 👌
[success] Android looking great! 👌

they both use the same approach
App.vue

<template>
  <ion-app>
    <ion-router-outlet />
  </ion-app>
</template>

<script >
import { IonApp, IonRouterOutlet } from '@ionic/vue';
import { defineComponent } from 'vue';
import { Device } from '@capacitor/device';
import i18n from './i18n'
//

export default defineComponent({
  name: 'App',
  components: {
    IonApp,
    IonRouterOutlet
  },
  methods: {
    loadLocaleMessages(language) {
      const locales = require.context('./i18n', true, /[A-Za-z0-9-_,\s]+\.json$/i)
      console.log("locales found=" + JSON.stringify(locales.keys()))
      const messages = {}
      locales.keys().forEach(key => {
        const matched = key.match(/([A-Za-z0-9-_]+)\./i)
        if (matched && matched.length > 1) {
          if (matched[1].startsWith(language)) {
            const locale = matched[1]
            messages[locale] = locales(key)
          }
        }
      })
      console.log(" returning language messages=" + JSON.stringify(messages)
      )
      return messages
    }  
  },
   mounted() {
    const device = Device;
    device.getLanguageTag().then((res) => {
      console.log('app Default lang', res.value);
      if (res.value.includes('?')) {
        const language = res.value.split('-')[0];
        this.$i18n.locale = language;
      } else {
        console.log("app setting default lang=" + res.value)
        try {
          this.$i18n.locale = res.value;
        }
        catch (error) {
          console.log("ap loading error ="+JSON.stringify(error))
        }
      }

      console.log("app loading messages for locale="+this.$i18n.locale)
      i18n.loadMessagesFor(this.$i18n.locale)
    })
  }, 
});
</script>

and the index.js from the i18 folder

import { createI18n } from 'vue-i18n'
import { nextTick } from 'vue'
//import { messages } from './messages'
//import { numberFormats } from './numbers'
//import { arabicPluralRules } from './plurals'
//import { datetimeFormats } from './datetimes'
// Set and expose the default locale
export const defaultLocale = 'en-US'
const msgs = { "fribble": "nothing" }
// Private instance of VueI18n object
let _i18n
// Initializer
function setup(options = { locale: defaultLocale, messages: msgs }) {
    _i18n = createI18n({
        locale: options.locale,
        fallbackLocale: defaultLocale,
        messages: options.messages,
  //      numberFormats,
  //      datetimeFormats,
  //      pluralizationRules: {
  //          'ar-EG': arabicPluralRules,
  //      },
    })
    setLocale(options.locale)
    return _i18n
}

async function loadMessagesFor(locale) {
    console.log("loading messages from ./i18n/" + locale + ".json")
    try {
        const messages = await import(
                "./" + locale + ".json"
        )
        console.log("index messages=" + JSON.stringify(messages))
        _i18n.global.setLocaleMessage(locale, messages.default)
    } catch (ex) {
        console.log("load error="+JSON.stringify(ex))
    }
    return nextTick()
}
// Sets the active locale. 
function setLocale(newLocale) {
    _i18n.global.locale = newLocale
}
// Public interface
export default {
    // Expose the VueI18n instance via a getter
    get vueI18n() {
        return _i18n
    },
    setup,
    setLocale,
    loadMessagesFor

}

the migrated one does translation successfully

the new one fails to load (import) the json file

 const messages = await import(
       "./" + locale + ".json"
        )
        console.log("index messages=" + JSON.stringify(messages))
        _i18n.global.setLocaleMessage(locale, messages.default)

on ios for the upgraded app I see these messages

  To Native ->  App addListener 116177886
⚡️  [log] - app Default lang en-US
⚡️  [log] - app setting default lang=en-US
⚡️  [log] - app loading messages for locale=en-US
⚡️  [log] - loading messages from ./en-US.json
⚡️  [log] - index messages={"startup":....

on ios for the newly created app I see

  [log] - app Default lang en-US
⚡️  [log] - app setting default lang=en-US
⚡️  [log] - app loading messages for locale=en-US
⚡️  [log] - loading messages from ./en-US.json
⚡️  [log] - load error={}

this wasn’t listed as a breaking change

i DID have the same folder linked into the projects src/i18n => i18n@ → …/…/cn-i18n/ , but on the NEW build run it used the source folder name off the link (./cn-i18n/en-US.json) , which doesn’t exist in the project folder
I had to copy the files to a real folder…

ONLY on the newly created project

i admit I created the project and then copied the src folder from the old v4 project in
everything else has worked without trouble
but the default App.vue doesn’t look changed

oh, and translation worked correctly on the upgraded app for both ios and android
doesn’t work for either on new project

i am using the lazy loading approach

ionic --version returns the version of Ionic CLI, that’s not your Ionic Framework version.
Can you check the version of @ionic/core package in your package.json? that the actual Ionic Framework version (you can also run ionic doctor and it will tell you your project information, including Ionic Framework version).

Not sure how the translation works internally, but if it uses XHR it’s broken for local files on latest Capacitor version, there is a PR with a fix, so could be that.

You could report an issue on Issues · ionic-team/capacitor · GitHub providing a sample app

thanks… its ionic info
on the working system, migrated

Ionic:

   Ionic CLI       : 7.2.0 (/usr/local/lib/node_modules/@ionic/cli)
   Ionic Framework : @ionic/vue 6.7.5

Capacitor:

   Capacitor CLI      : 6.0.0
   @capacitor/android : 6.0.0
   @capacitor/core    : 6.0.0
   @capacitor/ios     : 6.0.0

Utility:

   cordova-res : not installed globally
   native-run  : 2.0.1

System:

   NodeJS : v20.14.0 (/usr/local/bin/node)
   npm    : 10.7.0
   OS     : macOS Unknown

on the not working, started new

Ionic:

   Ionic CLI       : 7.2.0 (/usr/local/lib/node_modules/@ionic/cli)
   Ionic Framework : @ionic/vue 8.2.0

Capacitor:

   Capacitor CLI      : 6.0.0
   @capacitor/android : 6.0.0
   @capacitor/core    : 6.0.0
   @capacitor/ios     : 6.0.0

Utility:

   cordova-res : not installed globally
   native-run  : 2.0.1

System:

   NodeJS : v20.14.0 (/usr/local/bin/node)
   npm    : 10.8.1
   OS     : macOS Unknown

there is no @ionic/core in either package.json

before I open an issue, i always try to verify that I didn’t face plant

Your Ionic version is the @ionic/vue package version, first one was on version 6 and second on 8.

If both of them are in Capacitor 6 and one works and not the other then it isn’t a Capacitor issue.

I didn’t say it was a capacitor issue. I said it was an Ionic issue

I opened issue in framework

Yeah, I said it could be a Capacitor issue, not you. But if both apps are in the same Capacitor version then it’s not a Capacitor issue.

well, this turned out to be a change and a warning that flew by on the build

vite v5.2.12 building for production...
[plugin:vite:dynamic-import-vars] [plugin vite:dynamic-import-vars] src/i18n/index.js: 
invalid import "./${locale}.json". Variable imports cannot import their own directory, 
place imports in a separate directory or make the import filename more specific. 
For example: import(`./foo/${bar}.js`).

this warning doesn’t appear with the older syntax
‘./’+locale+‘.json’

so I moved the json files to a subfolder (locales) and it works
and I fixed the older to the same
i haven’t tried the linked folder yet

on the older migrated app the linked i18 folder worked ok
on the new build, vite seems to have lost its mind

> vite build
vite v5.2.12 building for production...
✓ 19 modules transformed.
x Build failed in 166ms
error during build:
[vite]: Rollup failed to resolve import "vue-i18n" from "/Users/sam/ionic/cn-i18n/index.js".  <--- note the linked to folder name

from a file list ls -laF src, shows the link

lrwxr-xr-x   1 root  staff    14 Jun  6 11:23 i18n@ -> ../../cn-i18n/

on the linke folder its a vite property , vite.config.ts

on the linked i18n folder, its a vite property
in vite.config.ts add

export default defineConfig({
  plugins: [
    vue(),
    legacy()
  ],
  resolve: {
    preserveSymlinks: true,   //<--- add
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
  test: {
    globals: true,
    environment: 'jsdom'
  }
})