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 Mar 23, 2023
1 parent fca552f commit 760fe46
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 0 deletions.
94 changes: 94 additions & 0 deletions lib/acx_sharded_intl.dart
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);
}
});
}
}
31 changes: 31 additions & 0 deletions test/acx_sharded_intl_test.dart
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'));
});
}

0 comments on commit 760fe46

Please sign in to comment.