OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 /** | 5 /// A library for general helper code associated with the intl library |
6 * A library for general helper code associated with the intl library | 6 /// rather than confined to specific parts of it. |
7 * rather than confined to specific parts of it. | |
8 */ | |
9 | 7 |
10 library intl_helpers; | 8 library intl_helpers; |
11 | 9 |
12 import 'dart:async'; | 10 import 'dart:async'; |
| 11 import 'package:intl/intl.dart'; |
13 | 12 |
14 /** | 13 /// Type for the callback action when a message translation is not found. |
15 * This is used as a marker for a locale data map that hasn't been initialized, | 14 typedef MessageIfAbsent(String message_str, List args); |
16 * and will throw an exception on any usage that isn't the fallback | 15 |
17 * patterns/symbols provided. | 16 /// This is used as a marker for a locale data map that hasn't been initialized, |
18 */ | 17 /// and will throw an exception on any usage that isn't the fallback |
19 class UninitializedLocaleData<F> { | 18 /// patterns/symbols provided. |
| 19 class UninitializedLocaleData<F> implements MessageLookup { |
20 final String message; | 20 final String message; |
21 final F fallbackData; | 21 final F fallbackData; |
22 const UninitializedLocaleData(this.message, this.fallbackData); | 22 UninitializedLocaleData(this.message, this.fallbackData); |
23 | 23 |
24 operator [](String key) => | 24 operator [](String key) => |
25 (key == 'en_US') ? fallbackData : _throwException(); | 25 (key == 'en_US') ? fallbackData : _throwException(); |
26 | 26 |
27 String lookupMessage(String message_str, [final String desc = '', | 27 /// If a message is looked up before any locale initialization, record it, |
28 final Map examples = const {}, String locale, String name, | 28 /// and throw an exception with that information once the locale is |
29 List<String> args, String meaning]) => message_str; | 29 /// initialized. |
| 30 /// |
| 31 /// Set this during development to find issues with race conditions between |
| 32 /// message caching and locale initialization. If the results of Intl.message |
| 33 /// calls aren't being cached, then this won't help. |
| 34 /// |
| 35 /// There's nothing that actually sets this, so checking this requires |
| 36 /// patching the code here. |
| 37 static final bool throwOnFallback = false; |
30 | 38 |
31 List get keys => _throwException(); | 39 /// The messages that were called before the locale was initialized. |
| 40 List<String> _badMessages = []; |
| 41 |
| 42 void _reportErrors() { |
| 43 if (throwOnFallback && _badMessages.length > 0) { |
| 44 throw new StateError( |
| 45 "The following messages were called before locale initialization:" |
| 46 " $_uninitializedMessages"); |
| 47 } |
| 48 } |
| 49 |
| 50 String get _uninitializedMessages => |
| 51 (_badMessages.toSet().toList()..sort()).join("\n "); |
| 52 |
| 53 String lookupMessage( |
| 54 String message_str, String locale, String name, List args, String meaning, |
| 55 {MessageIfAbsent ifAbsent}) { |
| 56 if (throwOnFallback) { |
| 57 _badMessages.add(name ?? message_str); |
| 58 } |
| 59 return message_str; |
| 60 } |
| 61 |
| 62 /// Given an initial locale or null, returns the locale that will be used |
| 63 /// for messages. |
| 64 String findLocale(String locale) => locale ?? Intl.getCurrentLocale(); |
| 65 |
| 66 List<String> get keys => _throwException() as List<String>; |
32 | 67 |
33 bool containsKey(String key) => (key == 'en_US') ? true : _throwException(); | 68 bool containsKey(String key) => (key == 'en_US') ? true : _throwException(); |
34 | 69 |
35 _throwException() { | 70 _throwException() { |
36 throw new LocaleDataException("Locale data has not been initialized" | 71 throw new LocaleDataException("Locale data has not been initialized" |
37 ", call $message."); | 72 ", call $message."); |
38 } | 73 } |
| 74 |
| 75 void addLocale(String localeName, Function findLocale) => _throwException(); |
| 76 } |
| 77 |
| 78 abstract class MessageLookup { |
| 79 String lookupMessage( |
| 80 String message_str, String locale, String name, List args, String meaning, |
| 81 {MessageIfAbsent ifAbsent}); |
| 82 void addLocale(String localeName, Function findLocale); |
39 } | 83 } |
40 | 84 |
41 class LocaleDataException implements Exception { | 85 class LocaleDataException implements Exception { |
42 final String message; | 86 final String message; |
43 LocaleDataException(this.message); | 87 LocaleDataException(this.message); |
44 toString() => "LocaleDataException: $message"; | 88 toString() => "LocaleDataException: $message"; |
45 } | 89 } |
46 | 90 |
47 /** | 91 /// An abstract superclass for data readers to keep the type system happy. |
48 * An abstract superclass for data readers to keep the type system happy. | |
49 */ | |
50 abstract class LocaleDataReader { | 92 abstract class LocaleDataReader { |
51 Future read(String locale); | 93 Future read(String locale); |
52 } | 94 } |
53 | 95 |
54 /** | 96 /// The internal mechanism for looking up messages. We expect this to be set |
55 * The internal mechanism for looking up messages. We expect this to be set | 97 /// by the implementing package so that we're not dependent on its |
56 * by the implementing package so that we're not dependent on its | 98 /// implementation. |
57 * implementation. | 99 MessageLookup messageLookup = |
58 */ | 100 new UninitializedLocaleData('initializeMessages(<locale>)', null); |
59 var messageLookup = | |
60 const UninitializedLocaleData('initializeMessages(<locale>)', null); | |
61 | 101 |
62 /** | 102 /// Initialize the message lookup mechanism. This is for internal use only. |
63 * Initialize the message lookup mechanism. This is for internal use only. | 103 /// User applications should import `message_lookup_by_library.dart` and call |
64 * User applications should import `message_lookup_by_library.dart` and call | 104 /// `initializeMessages` |
65 * `initializeMessages` | |
66 */ | |
67 void initializeInternalMessageLookup(Function lookupFunction) { | 105 void initializeInternalMessageLookup(Function lookupFunction) { |
68 if (messageLookup is UninitializedLocaleData) { | 106 if (messageLookup is UninitializedLocaleData) { |
| 107 // This line has to be precisely this way to work around an analyzer crash. |
| 108 (messageLookup as UninitializedLocaleData)._reportErrors(); |
69 messageLookup = lookupFunction(); | 109 messageLookup = lookupFunction(); |
70 } | 110 } |
71 } | 111 } |
| 112 |
| 113 /// If a message is a string literal without interpolation, compute |
| 114 /// a name based on that and the meaning, if present. |
| 115 // NOTE: THIS LOGIC IS DUPLICATED IN intl_translation AND THE TWO MUST MATCH. |
| 116 String computeMessageName(String name, String text, String meaning) { |
| 117 if (name != null && name != "") return name; |
| 118 return meaning == null ? text : "${text}_${meaning}"; |
| 119 } |
OLD | NEW |