i18n icon indicating copy to clipboard operation
i18n copied to clipboard

Multiple Translations

Open jorgjanke opened this issue 10 years ago • 14 comments

If in your application you import a package, which is translated and in your application create a translation, you get:

Duplicated library name 'messages_all'. library messages_all;

when using the generate_from_arb functionality. This applies also to the library in the translated files, e.g. messages_es

The cause is that in generate_localized.dart, the library name(s) are hardcoded.

generate_from_arb.dart allows to specify a file prefix, e.g. --generated-file-prefix=base-

I suggest to use the file prefix also for the library names.

jorgjanke avatar Jun 05 '15 03:06 jorgjanke

Right now the intl package only supports a single set of translations within an app. Are you saying that it actually works to have multiple translations, and you're just getting warnings about the library name?

On Thu, Jun 4, 2015 at 8:40 PM Jorg Janke [email protected] wrote:

If in your application you import a package, which is translated and in your application create a translation, you get:

Duplicated library name 'messages_all'. library messages_all;

when using the generate_from_arb functionality. This applies also to the library in the translated files, e.g. messages_es

The cause is that in generate_localized.dart, the library name(s) are hardcoded.

generate_from_arb.dart allows to specify a file prefix, e.g. --generated-file-prefix=base-

I suggest to use the file prefix also for the library names.

— Reply to this email directly or view it on GitHub https://github.com/dart-lang/i18n/issues/358.

alan-knight avatar Jun 19 '15 21:06 alan-knight

One translation per library. When I import a library which has a translation into an application which has a translation, I get the Warning: Duplicated library name 'messages_all'. .. etc. messages - What are the options to deal with this (not unusual) situation?

jorgjanke avatar Aug 04 '15 00:08 jorgjanke

Duplicated library names are only a warning, which can be ignored. But if you actually try to use it I suspect that it will fail for other reasons.

alan-knight avatar Aug 04 '15 00:08 alan-knight

The objective was to create an enhancement request - and if it is accepted, I might attempt to fix it by adding the prefix to avoid the name clash.

jorgjanke avatar Aug 04 '15 17:08 jorgjanke

The enhancement request is reasonable. Changing the name to reflect that. However, the problem is not the duplicated library name. That should probably be fixed to match the file name. But it's probably more important that the program will not run correctly if it has two different sets of translated messages in it. In particular, only the first one that gets initialized for a particular locale will be used, and message in the other will either fail or will use the translation from the first catalog. I thought there was already an open issue for that, but I don't see one, so we can use this one.

alan-knight avatar Aug 04 '15 20:08 alan-knight

I have this problem as well. My app has some of its functionality implemented as an independent package that should be providing its own localized strings; I had to resort to the following dirty hack on the package side to allow its lookup table to coexist with the application's:

import 'dart:async';
import 'dart:ui';

import 'package:flutter/widgets.dart';
import 'package:my_package/l10n/messages_all.dart';
import 'package:intl/intl.dart' as RealIntl; // HORRIBLE HACK! (see below)
import 'package:intl/src/intl_helpers.dart';

class MyPackageLocalizationsDelegate
    extends LocalizationsDelegate<MyPackageLocalizations> {
  const MyPackageLocalizationsDelegate();

  @override
  bool isSupported(Locale locale) => ... ;

  @override
  Future<MyPackageLocalizations> load(Locale locale) =>
      MyPackageLocalizations.load(locale);

  @override
  bool shouldReload(LocalizationsDelegate<MyPackageLocalizations> old) => false;
}

/// HORRIBLE HACK!
///
/// The intl package only supports loading one global message table, which is
/// held at [messageLookup] in intl_helpers.dart. This prevents us from
/// providing localized strings in this plugin for consumption in a parent
/// application, as only one message table will be used.
///
/// The travesty below works around this. It is so horrible because we are
/// trying to have our cake and eat it too:
///
/// - We want to manage strings in this package with the intl_translation
///   extraction and merging tools, so message lookup calls must (appear) to be
///   made to [Intl]
/// - We don't want to edit generated files (messages_all.dart, etc.) by hand
///
/// How it works:
///
/// 1. We define our own [Intl] class that calls into our own lookup,
///    [myMessageLookup]. The lookup is initially "empty".
/// 2. We import the real intl class under an alias and delegate to it where
///    appropriate.
/// 3. We call the default generated [initializeMessages], then immediately swap
///    out the real [messageLookup] and [myMessageLookup] objects. This
///    returns the real lookup to an uninitialized state for the application's
///    localizations to load. (Yes, all this relies on localization delegate
///    init happening sequentially.)
/// 4. We exhort the consuming application to load our localization delegate
///    before the application's.
class Intl {
  // copied from the real intl package
  static String message(String message_str,
          {String desc: '',
          Map<String, dynamic> examples: const {},
          String locale,
          String name,
          List args,
          String meaning,
          bool skip}) =>
      _message(message_str, locale, name, args, meaning);

  // copied from the real intl package
  static _message(String message_str, String locale, String name, List args,
      String meaning) {
    return myMessageLookup.lookupMessage(
        message_str, locale, name, args, meaning);
  }
}

MessageLookup myMessageLookup =
    UninitializedLocaleData('initializeMessages(<locale>)', null);

class MyPackageLocalizations {

  static Future<MyPackageLocalizations> load(Locale locale) {
    final name =
        locale.countryCode == null ? locale.languageCode : locale.toString();
    final localeName = RealIntl.Intl.canonicalizedLocale(name);
    return initializeMessages(localeName).then((_) {
      // HORRIBLE HACK! (see above)
      // Steal the just-initialized lookup and keep it for ourselves.
      // Then un-initialize the real lookup so the next localizations can load.
      final uninitialized = myMessageLookup;
      myMessageLookup = messageLookup;
      messageLookup = uninitialized;
      RealIntl.Intl.defaultLocale = localeName;
      return MyPackageLocalizations();
    });
  }

⋮

}

amake avatar Mar 19 '19 05:03 amake

This is pretty unexpected and dangerously hidden limitation to allow only one translation set per app. I would not have expected to face this kind of limitation when developing 3 "totally independent" packages each having their own translation set. It took 3 months until I realized this issue... mainly because the default language works normally. You will see the issue only after changing the language.

kinex avatar Apr 15 '19 13:04 kinex

My workaround no longer functions, it seems due to changes in the timing of when localization delegates are initialized by the framework.

I've managed to hack it again, but I'm not sure if it only works by coincidence since it relies on a specific order of execution: https://github.com/amake/flutter_l10n_test/commit/6ff8fc632ae728e299e76b38f947f6f2d687ce7e

amake avatar Jul 29 '19 15:07 amake

To have localizations in my packages I'm using a patch that I explain in this article. https://medium.com/@gerrel/flutter-package-with-intl-localization-394e9da1164a?source=friends_link&sk=48169f60f86aef7eb38bf0fe43041574

Gerrel avatar Dec 27 '19 16:12 Gerrel

Is there a better solution?

lizhuoyuan avatar Jan 04 '21 02:01 lizhuoyuan

I use https://github.com/Innim/flutter_multiple_localization to load and override messages

goxiaoy avatar May 13 '23 06:05 goxiaoy

It’s been 9 years and this problem still exists 😭

azhon avatar Jan 25 '24 14:01 azhon

It’s been 9 years and this problem still exists 😭

Which is why we are working on it in package:messages! :)

mosuem avatar Jan 25 '24 15:01 mosuem

It’s been 9 years and this problem still exists 😭

Which is why we are working on it in package:messages! :)

I found the code that caused the current bug in the source code of intl package

Since the messageLookup used by multiple packages is the same object, it causes other packages to return when they are initialized.

image image

azhon avatar Jan 26 '24 09:01 azhon