This repository has been archived by the owner on Aug 30, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 157
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PiperOrigin-RevId: 518316023
- Loading branch information
Googler
authored and
Copybara-Service
committed
Mar 23, 2023
1 parent
fca552f
commit 760fe46
Showing
2 changed files
with
125 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
/// This library provides storage for localized messages in a lookup table. | ||
/// This is used for optimizing localized string lookups for web | ||
/// development when using ACX sharded localization (go/acx-localization). | ||
/// This library relies on javascript features. | ||
/// Dart2js generates calls to this library when using sharded localization; | ||
/// clients shouldn't make the call to this library directly. | ||
library intl_message_table; | ||
|
||
import 'dart:convert'; | ||
import 'dart:js_util' as js_util; | ||
|
||
/// Internal representation of a translated string; either a single string (for | ||
/// non-constructed strings) or an array of strings and argument indexes. | ||
/// For example, the constructed string 'My name is ${name}, how are you?' | ||
/// is represented as ['My name is ', 0, ', how are you?']. | ||
class _Message { | ||
// Each element is either a string or an integer representing the argument index. | ||
final List<dynamic> values; | ||
|
||
_Message(this.values); | ||
|
||
_Message.fromString(String string) : this([string]); | ||
|
||
// Most messages consist of only a single string; only constructed strings | ||
// have more than one element. This function is for non-constructed strings. | ||
String getString() { | ||
return values.first.toString(); | ||
} | ||
} | ||
|
||
/// Functions for loading translations for messages based on an index. | ||
/// Each compilation unit registers the translations it needs in a field on the | ||
/// Dom under a unique id for it's app. These strings are referenced by index in | ||
/// the code. When a string is referenced, the translations for that app are | ||
/// inserted into the map. | ||
class AcxIntlMessageTable { | ||
static List<_Message?> _internalMap = []; | ||
static String lookup(int id) { | ||
return _lookupMessagebyId(id).getString(); | ||
} | ||
|
||
static List<dynamic> lookupArray(int id) => _lookupMessagebyId(id).values; | ||
|
||
static _Message? getMessage(int id) => | ||
id < _internalMap.length ? _internalMap[id] : null; | ||
|
||
static _Message _lookupMessagebyId(int id) { | ||
var message = getMessage(id); | ||
if (message != null) { | ||
return message; | ||
} | ||
// Copy outstanding messages locally | ||
// TODO(b/274616739): Use appId provided by environment. | ||
// Maybe something like: | ||
// const String.fromEnvironment('dart.dart2js.appid'); | ||
final appId = 'MyAppId'; | ||
|
||
for (var message in _popPendingTranslations(appId)) { | ||
loadMessages(message); | ||
} | ||
message = getMessage(id); | ||
if (message == null) { | ||
throw ArgumentError('Unable to locate string $id'); | ||
} | ||
return message; | ||
} | ||
|
||
/// Translations for a language are lazily parsed into the map when requested on | ||
/// a per-app basis. | ||
static List<String> _popPendingTranslations(String appId) { | ||
final result = js_util.getProperty<List<String>>( | ||
js_util.getProperty(js_util.globalThis, r'$_intl_pending_translations'), | ||
appId); | ||
js_util.setProperty<List<String>>( | ||
js_util.getProperty(js_util.globalThis, r'$_intl_pending_translations'), | ||
appId, []); | ||
return result; | ||
} | ||
|
||
static void loadMessages(String json_messages) { | ||
var parsed = json.decode(json_messages); | ||
parsed.forEach((k, v) { | ||
int? index = int.tryParse(k); | ||
if (index != null) { | ||
// Grow array to fit in new message. | ||
if (index >= _internalMap.length) _internalMap.length = index + 1; | ||
_internalMap[index] = _Message(v); | ||
} | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import 'package:intl/acx_sharded_intl.dart'; | ||
import 'package:test/test.dart'; | ||
import 'dart:js_util' as js_util; | ||
import 'dart:convert'; | ||
|
||
void main() { | ||
setUp(() { | ||
final testStrings1 = { | ||
'0': ['Test String 0'], | ||
'1': ['Test String 1'], | ||
'2': ['Test String 2'] | ||
}; | ||
final testStrings2 = { | ||
'3': ['Test String 3'], | ||
'4': ['Test String 4'], | ||
'5': ['Test String 5'] | ||
}; | ||
final encoding1 = jsonEncode(testStrings1); | ||
final encoding2 = jsonEncode(testStrings2); | ||
js_util.setProperty<Object>(js_util.globalThis, | ||
r'$_intl_pending_translations', js_util.newObject()); | ||
js_util.setProperty( | ||
js_util.getProperty(js_util.globalThis, r'$_intl_pending_translations'), | ||
'MyAppId', | ||
[encoding1, encoding2]); | ||
}); | ||
test('testIntlMessageTableLookup', () { | ||
expect(AcxIntlMessageTable.lookup(1), equals('Test String 1')); | ||
expect(AcxIntlMessageTable.lookup(5), equals('Test String 5')); | ||
}); | ||
} |