Skip to content
This repository has been archived by the owner on Aug 30, 2023. It is now read-only.

Commit

Permalink
Internal change
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 518316023
  • Loading branch information
Googler authored and Copybara-Service committed Apr 14, 2023
1 parent d8844a0 commit 6fc8004
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 0 deletions.
101 changes: 101 additions & 0 deletions lib/acx_sharded_intl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/// 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?'].
/// This representation matches the code generated by the 'generate_from_xtb'
/// tool.
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();
}

List<dynamic> getList() {
return values;
}
}

/// 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 its app as a json string. Messages are referenced
/// by index in the code. When a message is referenced, the translations for
/// that app are parsed and inserted into the table.
class AcxIntlMessageTable {
// Indexed list of messages. If a message isn't yet loaded, the entry will be
// null.
static List<_Message?> _internalArray = [];

static String lookup(int id) {
return _lookupMessagebyId(id).getString();
}

static List<dynamic> lookupList(int id) => _lookupMessagebyId(id).getList();

static _Message? _getMessage(int id) =>
id < _internalArray.length ? _internalArray[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((index_string, message) {
int index = int.parse(index_string);
// Grow array to fit in new message.
if (index >= _internalArray.length) _internalArray.length = index + 1;
_internalArray[index] = _Message(message);
});
}
}
35 changes: 35 additions & 0 deletions test/acx_sharded_intl_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
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'],
'6': ['Constructed ', 0, ' String 6']
};
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'));
expect(AcxIntlMessageTable.lookup(5), equals('Test String 5'));
expect(AcxIntlMessageTable.lookupList(6),
equals(['Constructed ', 0, ' String 6']));
});
}

0 comments on commit 6fc8004

Please sign in to comment.