Bug 1613705 - [localization] part1: Introduce localization-ffi bindings for fluent-fallback. r=emilio,nika

Depends on D117349

Differential Revision: https://phabricator.services.mozilla.com/D104788
This commit is contained in:
Zibi Braniecki 2021-08-03 16:25:10 +00:00
parent 4dbd59d465
commit 05db766a87
12 changed files with 674 additions and 50 deletions

23
Cargo.lock generated
View File

@ -2013,6 +2013,7 @@ dependencies = [
"l10nregistry",
"l10nregistry-ffi",
"lmdb-rkv-sys",
"localization-ffi",
"log",
"mapped_hyph",
"mdns_service",
@ -2809,6 +2810,28 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "localization-ffi"
version = "0.1.0"
dependencies = [
"async-trait",
"cstr",
"fluent",
"fluent-fallback",
"fluent-ffi",
"futures 0.3.15",
"futures-channel",
"l10nregistry",
"l10nregistry-ffi",
"libc",
"moz_task",
"nserror",
"nsstring",
"thin-vec",
"unic-langid",
"xpcom",
]
[[package]]
name = "lock_api"
version = "0.4.4"

View File

@ -188,35 +188,39 @@ bool extendJSArrayWithErrors(JSContext* aCx, JS::Handle<JSObject*> aErrors,
return true;
}
static void ConvertArgs(const L10nArgs& aArgs,
nsTArray<ffi::L10nArg>& aRetVal) {
for (const auto& entry : aArgs.Entries()) {
if (!entry.mValue.IsNull()) {
const auto& value = entry.mValue.Value();
if (value.IsUTF8String()) {
aRetVal.AppendElement(ffi::L10nArg{
&entry.mKey,
ffi::FluentArgument::String(&value.GetAsUTF8String())});
} else {
aRetVal.AppendElement(ffi::L10nArg{
&entry.mKey, ffi::FluentArgument::Double_(value.GetAsDouble())});
}
}
}
}
void FluentBundle::FormatPattern(JSContext* aCx, const FluentPattern& aPattern,
const Nullable<L10nArgs>& aArgs,
const Optional<JS::Handle<JSObject*>>& aErrors,
nsACString& aRetVal, ErrorResult& aRv) {
nsTArray<nsCString> argIds;
nsTArray<ffi::FluentArgument> argValues;
nsTArray<ffi::L10nArg> l10nArgs;
if (!aArgs.IsNull()) {
const L10nArgs& args = aArgs.Value();
for (auto& entry : args.Entries()) {
if (!entry.mValue.IsNull()) {
argIds.AppendElement(entry.mKey);
auto& value = entry.mValue.Value();
if (value.IsUTF8String()) {
argValues.AppendElement(
ffi::FluentArgument::String(&value.GetAsUTF8String()));
} else {
argValues.AppendElement(
ffi::FluentArgument::Double_(value.GetAsDouble()));
}
}
}
ConvertArgs(args, l10nArgs);
}
nsTArray<nsCString> errors;
bool succeeded = fluent_bundle_format_pattern(mRaw.get(), &aPattern.mId,
&aPattern.mAttrName, &argIds,
&argValues, &aRetVal, &errors);
&aPattern.mAttrName, &l10nArgs,
&aRetVal, &errors);
if (!succeeded) {
return aRv.ThrowInvalidStateError(

View File

@ -0,0 +1,26 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_intl_l10n_LocalizationBindings_h
#define mozilla_intl_l10n_LocalizationBindings_h
#include "mozilla/intl/localization_ffi_generated.h"
#include "mozilla/RefPtr.h"
namespace mozilla {
template <>
struct RefPtrTraits<intl::ffi::LocalizationRc> {
static void AddRef(const intl::ffi::LocalizationRc* aPtr) {
intl::ffi::localization_addref(aPtr);
}
static void Release(const intl::ffi::LocalizationRc* aPtr) {
intl::ffi::localization_release(aPtr);
}
};
} // namespace mozilla
#endif

View File

@ -11,6 +11,7 @@ EXPORTS.mozilla.intl += [
"FluentResource.h",
"L10nRegistry.h",
"Localization.h",
"LocalizationBindings.h",
"RegistryBindings.h",
]
@ -46,13 +47,19 @@ USE_LIBS += ["intlcomponents"]
if CONFIG["COMPILE_ENVIRONMENT"]:
CbindgenHeader("fluent_ffi_generated.h", inputs=["/intl/l10n/rust/fluent-ffi"])
CbindgenHeader(
"l10nregistry_ffi_generated.h", inputs=["/intl/l10n/rust/l10nregistry-ffi"]
)
CbindgenHeader(
"localization_ffi_generated.h", inputs=["/intl/l10n/rust/localization-ffi"]
)
EXPORTS.mozilla.intl += [
"!fluent_ffi_generated.h",
"!l10nregistry_ffi_generated.h",
"!localization_ffi_generated.h",
]
XPCSHELL_TESTS_MANIFESTS += ["test/xpcshell.ini"]

View File

@ -20,9 +20,16 @@ pub type FluentBundleRc = FluentBundle<Rc<FluentResource>>;
#[derive(Debug)]
#[repr(C, u8)]
pub enum FluentArgument {
pub enum FluentArgument<'s> {
Double_(f64),
String(*const nsCString),
String(&'s nsACString),
}
#[derive(Debug)]
#[repr(C)]
pub struct L10nArg<'s> {
pub id: &'s nsACString,
pub value: FluentArgument<'s>,
}
fn transform_accented(s: &str) -> Cow<str> {
@ -149,14 +156,12 @@ pub fn adapt_bundle_for_gecko(bundle: &mut FluentBundleRc, pseudo_strategy: Opti
}
#[no_mangle]
pub unsafe extern "C" fn fluent_bundle_new_single(
pub extern "C" fn fluent_bundle_new_single(
locale: &nsACString,
use_isolating: bool,
pseudo_strategy: &nsACString,
) -> *mut FluentBundleRc {
// We can use as_str_unchecked because this string comes from WebIDL and is
// guaranteed utf-8.
let id = match locale.as_str_unchecked().parse::<LanguageIdentifier>() {
let id = match locale.to_utf8().parse::<LanguageIdentifier>() {
Ok(id) => id,
Err(..) => return std::ptr::null_mut(),
};
@ -178,7 +183,7 @@ pub unsafe extern "C" fn fluent_bundle_new(
let mut langids = Vec::with_capacity(locale_count);
let locales = std::slice::from_raw_parts(locales, locale_count);
for locale in locales {
let id = match locale.as_str_unchecked().parse::<LanguageIdentifier>() {
let id = match locale.to_utf8().parse::<LanguageIdentifier>() {
Ok(id) => id,
Err(..) => return std::ptr::null_mut(),
};
@ -228,13 +233,13 @@ pub extern "C" fn fluent_bundle_has_message(bundle: &FluentBundleRc, id: &nsACSt
}
#[no_mangle]
pub unsafe extern "C" fn fluent_bundle_get_message(
pub extern "C" fn fluent_bundle_get_message(
bundle: &FluentBundleRc,
id: &nsACString,
has_value: &mut bool,
attrs: &mut ThinVec<nsCString>,
) -> bool {
match bundle.get_message(id.as_str_unchecked()) {
match bundle.get_message(&id.to_utf8()) {
Some(message) => {
attrs.reserve(message.attributes().count());
*has_value = message.value().is_some();
@ -251,24 +256,23 @@ pub unsafe extern "C" fn fluent_bundle_get_message(
}
#[no_mangle]
pub unsafe extern "C" fn fluent_bundle_format_pattern(
pub extern "C" fn fluent_bundle_format_pattern(
bundle: &FluentBundleRc,
id: &nsACString,
attr: &nsACString,
arg_ids: &ThinVec<nsCString>,
arg_vals: &ThinVec<FluentArgument>,
args: &ThinVec<L10nArg>,
ret_val: &mut nsACString,
ret_errors: &mut ThinVec<nsCString>,
) -> bool {
let args = convert_args(arg_ids, arg_vals);
let args = convert_args(&args);
let message = match bundle.get_message(id.as_str_unchecked()) {
let message = match bundle.get_message(&id.to_utf8()) {
Some(message) => message,
None => return false,
};
let pattern = if !attr.is_empty() {
match message.get_attribute(attr.as_str_unchecked()) {
match message.get_attribute(&attr.to_utf8()) {
Some(attr) => attr.value(),
None => return false,
}
@ -304,25 +308,20 @@ pub unsafe extern "C" fn fluent_bundle_add_resource(
}
}
fn convert_args<'a>(
arg_ids: &'a [nsCString],
arg_vals: &'a [FluentArgument],
) -> Option<FluentArgs<'a>> {
debug_assert_eq!(arg_ids.len(), arg_vals.len());
if arg_ids.is_empty() {
pub fn convert_args<'s>(args: &[L10nArg<'s>]) -> Option<FluentArgs<'s>> {
if args.is_empty() {
return None;
}
let mut args = FluentArgs::with_capacity(arg_ids.len());
for (id, val) in arg_ids.iter().zip(arg_vals.iter()) {
let val = match val {
let mut result = FluentArgs::with_capacity(args.len());
for arg in args {
let val = match arg.value {
FluentArgument::Double_(d) => FluentValue::from(d),
FluentArgument::String(s) => FluentValue::from(unsafe { (**s).to_string() }),
FluentArgument::String(s) => FluentValue::from(s.to_utf8()),
};
args.set(id.to_string(), val);
result.set(arg.id.to_string(), val);
}
Some(args)
Some(result)
}
fn append_fluent_errors_to_ret_errors(ret_errors: &mut ThinVec<nsCString>, errors: &[FluentError]) {

View File

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cstr::cstr;
use fluent_fallback::env::LocalesProvider;
use l10nregistry::{
env::ErrorReporter,
errors::{L10nRegistryError, L10nRegistrySetupError},
@ -68,6 +69,13 @@ impl ErrorReporter for GeckoEnvironment {
}
}
impl LocalesProvider for GeckoEnvironment {
type Iter = std::vec::IntoIter<unic_langid::LanguageIdentifier>;
fn locales(&self) -> Self::Iter {
vec!["en-US".parse().unwrap()].into_iter()
}
}
fn log_simple_console_error(
error: &impl fmt::Display,
category: &CStr,

View File

@ -2,9 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
mod env;
pub mod env;
mod fetcher;
pub mod load;
mod registry;
pub mod registry;
mod source;
mod xpcom_utils;

View File

@ -0,0 +1,23 @@
[package]
name = "localization-ffi"
version = "0.1.0"
authors = ["nobody@mozilla.org"]
edition = "2018"
[dependencies]
futures-channel = "0.3"
futures = "0.3"
libc = "0.2"
nserror = { path = "../../../../xpcom/rust/nserror" }
nsstring = { path = "../../../../xpcom/rust/nsstring" }
l10nregistry = { git = "https://github.com/mozilla/l10nregistry-rs", rev = "55bf7f826d773303a67d8d7fdab099a04322d4fb" }
fluent = { version = "0.16", features = ["fluent-pseudo"] }
unic-langid = "0.9"
thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
async-trait = "0.1"
moz_task = { path = "../../../../xpcom/rust/moz_task" }
fluent-ffi = { path = "../fluent-ffi" }
fluent-fallback = "0.5"
l10nregistry-ffi = { path = "../l10nregistry-ffi" }
xpcom = { path = "../../../../xpcom/rust/xpcom" }
cstr = "0.2"

View File

@ -0,0 +1,25 @@
header = """/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */"""
autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. See RunCbindgen.py */
#ifndef mozilla_intl_l10n_LocalizationBindings_h
#error "Don't include this file directly, instead include LocalizationBindings.h"
#endif
"""
include_version = true
braces = "SameLine"
line_length = 100
tab_width = 2
language = "C++"
namespaces = ["mozilla", "intl", "ffi"]
[parse]
parse_deps = true
include = ["fluent-fallback"]
[enum]
derive_helper_methods = true
[export.rename]
"ThinVec" = "nsTArray"
"Promise" = "dom::Promise"

View File

@ -0,0 +1,508 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use fluent::FluentValue;
use fluent_fallback::{
types::{
L10nAttribute as FluentL10nAttribute, L10nKey as FluentL10nKey,
L10nMessage as FluentL10nMessage,
},
Localization,
};
use fluent_ffi::{convert_args, FluentArgs, FluentArgument, L10nArg};
use l10nregistry_ffi::{
env::GeckoEnvironment,
registry::{get_l10n_registry, GeckoL10nRegistry},
};
use nserror::{nsresult, NS_OK};
use nsstring::{nsACString, nsCString};
use std::{borrow::Cow, cell::RefCell};
use thin_vec::ThinVec;
use xpcom::{interfaces::nsrefcnt, RefCounted, RefPtr, Refcnt};
#[derive(Debug)]
#[repr(C)]
pub struct L10nKey<'s> {
id: &'s nsACString,
args: ThinVec<L10nArg<'s>>,
}
impl<'s> From<&'s L10nKey<'s>> for FluentL10nKey<'static> {
fn from(input: &'s L10nKey<'s>) -> Self {
FluentL10nKey {
id: input.id.to_utf8().to_string().into(),
args: convert_args_to_owned(&input.args),
}
}
}
// This is a variant of `convert_args` from `fluent-ffi` with a 'static constrain
// put on the resulting `FluentArgs` to make it acceptable into `spqwn_current_thread`.
pub fn convert_args_to_owned(args: &[L10nArg]) -> Option<FluentArgs<'static>> {
if args.is_empty() {
return None;
}
let mut result = FluentArgs::with_capacity(args.len());
for arg in args {
let val = match arg.value {
FluentArgument::Double_(d) => FluentValue::from(d),
// We need this to be owned because we pass the result into `spawn_current_thread`.
FluentArgument::String(s) => FluentValue::from(Cow::Owned(s.to_utf8().to_string())),
};
result.set(arg.id.to_string(), val);
}
Some(result)
}
#[derive(Debug)]
#[repr(C)]
pub struct L10nAttribute {
name: nsCString,
value: nsCString,
}
impl From<FluentL10nAttribute<'_>> for L10nAttribute {
fn from(attr: FluentL10nAttribute<'_>) -> Self {
Self {
name: nsCString::from(&*attr.name),
value: nsCString::from(&*attr.value),
}
}
}
#[derive(Debug)]
#[repr(C)]
pub struct L10nMessage {
value: nsCString,
attributes: ThinVec<L10nAttribute>,
}
impl std::default::Default for L10nMessage {
fn default() -> Self {
Self {
value: nsCString::new(),
attributes: ThinVec::new(),
}
}
}
#[derive(Debug)]
#[repr(C)]
pub struct OptionalL10nMessage {
is_present: bool,
message: L10nMessage,
}
impl From<FluentL10nMessage<'_>> for L10nMessage {
fn from(input: FluentL10nMessage) -> Self {
let value = if let Some(value) = input.value {
value.to_string().into()
} else {
let mut s = nsCString::new();
s.set_is_void(true);
s
};
Self {
value,
attributes: input.attributes.into_iter().map(Into::into).collect(),
}
}
}
pub struct LocalizationRc {
inner: RefCell<Localization<GeckoL10nRegistry, GeckoEnvironment>>,
refcnt: Refcnt,
}
// xpcom::RefPtr support
unsafe impl RefCounted for LocalizationRc {
unsafe fn addref(&self) {
localization_addref(self);
}
unsafe fn release(&self) {
localization_release(self);
}
}
impl LocalizationRc {
pub fn new(reg: &GeckoL10nRegistry, res_ids: Vec<String>, is_sync: bool) -> RefPtr<Self> {
let inner = Localization::with_env(res_ids, is_sync, GeckoEnvironment, reg.clone());
let loc = Box::new(LocalizationRc {
inner: RefCell::new(inner),
refcnt: unsafe { Refcnt::new() },
});
unsafe {
RefPtr::from_raw(Box::into_raw(loc))
.expect("Failed to create RefPtr<LocalizationRc> from Box<LocalizationRc>")
}
}
pub fn add_resource_id(&self, res_id: String) {
self.inner.borrow_mut().add_resource_id(res_id);
}
pub fn add_resource_ids(&self, res_ids: Vec<String>) {
self.inner.borrow_mut().add_resource_ids(res_ids);
}
pub fn remove_resource_id(&self, res_id: String) -> usize {
self.inner.borrow_mut().remove_resource_id(res_id)
}
pub fn remove_resource_ids(&self, res_ids: Vec<String>) -> usize {
self.inner.borrow_mut().remove_resource_ids(res_ids)
}
/// Upgrade synchronous mode to asynchronous.
pub fn upgrade(&self) {
if self.is_sync() {
self.inner.borrow_mut().set_async();
}
}
pub fn is_sync(&self) -> bool {
self.inner.borrow().is_sync()
}
pub fn format_value_sync(
&self,
id: &nsACString,
args: &ThinVec<L10nArg>,
ret_val: &mut nsACString,
ret_err: &mut ThinVec<nsCString>,
) -> bool {
let mut errors = vec![];
let args = convert_args(&args);
if let Ok(value) = self.inner.borrow().bundles().format_value_sync(
&id.to_utf8(),
args.as_ref(),
&mut errors,
) {
if let Some(value) = value {
ret_val.assign(&value);
} else {
ret_val.set_is_void(true);
}
ret_err.extend(errors.into_iter().map(|err| err.to_string().into()));
true
} else {
false
}
}
pub fn format_values_sync(
&self,
keys: &ThinVec<L10nKey>,
ret_val: &mut ThinVec<nsCString>,
ret_err: &mut ThinVec<nsCString>,
) -> bool {
ret_val.reserve(keys.len());
let keys: Vec<FluentL10nKey> = keys.into_iter().map(|k| k.into()).collect();
let mut errors = vec![];
if let Ok(values) = self
.inner
.borrow()
.bundles()
.format_values_sync(&keys, &mut errors)
{
for value in values.iter() {
if let Some(value) = value {
ret_val.push(value.as_ref().into());
} else {
let mut void_string = nsCString::new();
void_string.set_is_void(true);
ret_val.push(void_string);
}
}
ret_err.extend(errors.into_iter().map(|err| err.to_string().into()));
true
} else {
false
}
}
pub fn format_messages_sync(
&self,
keys: &ThinVec<L10nKey>,
ret_val: &mut ThinVec<OptionalL10nMessage>,
ret_err: &mut ThinVec<nsCString>,
) -> bool {
ret_val.reserve(keys.len());
let mut errors = vec![];
let keys: Vec<FluentL10nKey> = keys.into_iter().map(|k| k.into()).collect();
if let Ok(messages) = self
.inner
.borrow()
.bundles()
.format_messages_sync(&keys, &mut errors)
{
for msg in messages {
ret_val.push(if let Some(msg) = msg {
OptionalL10nMessage {
is_present: true,
message: msg.into(),
}
} else {
OptionalL10nMessage {
is_present: false,
message: L10nMessage::default(),
}
});
}
assert_eq!(keys.len(), ret_val.len());
ret_err.extend(errors.into_iter().map(|err| err.to_string().into()));
true
} else {
false
}
}
pub fn format_value(
&self,
id: &nsACString,
args: &ThinVec<L10nArg>,
promise: &xpcom::Promise,
callback: extern "C" fn(&xpcom::Promise, &nsACString, &ThinVec<nsCString>),
) {
let bundles = self.inner.borrow().bundles().clone();
let args = convert_args_to_owned(&args);
let id = nsCString::from(id);
let strong_promise = RefPtr::new(promise);
moz_task::spawn_current_thread(async move {
let mut errors = vec![];
let value = if let Some(value) = bundles
.format_value(&id.to_utf8(), args.as_ref(), &mut errors)
.await
{
let v: nsCString = value.to_string().into();
v
} else {
let mut v = nsCString::new();
v.set_is_void(true);
v
};
let errors = errors
.into_iter()
.map(|err| err.to_string().into())
.collect();
callback(&strong_promise, &value, &errors);
})
.expect("Failed to spawn future");
}
pub fn format_values(
&self,
keys: &ThinVec<L10nKey>,
promise: &xpcom::Promise,
callback: extern "C" fn(&xpcom::Promise, Option<&ThinVec<nsCString>>),
) {
let bundles = self.inner.borrow().bundles().clone();
let keys: Vec<FluentL10nKey> = keys.into_iter().map(|k| k.into()).collect();
let strong_promise = RefPtr::new(promise);
moz_task::spawn_current_thread(async move {
let mut errors = vec![];
let ret_val = bundles
.format_values(&keys, &mut errors)
.await
.into_iter()
.map(|value| {
if let Some(value) = value {
nsCString::from(value.as_ref())
} else {
let mut v = nsCString::new();
v.set_is_void(true);
v
}
})
.collect::<ThinVec<_>>();
callback(&strong_promise, Some(&ret_val));
})
.expect("Failed to spawn future");
}
pub fn format_messages(
&self,
keys: &ThinVec<L10nKey>,
promise: &xpcom::Promise,
callback: extern "C" fn(
&xpcom::Promise,
Option<&ThinVec<OptionalL10nMessage>>,
&ThinVec<nsCString>,
),
) {
let bundles = self.inner.borrow().bundles().clone();
let keys: Vec<FluentL10nKey> = keys.into_iter().map(|k| k.into()).collect();
let strong_promise = RefPtr::new(promise);
moz_task::spawn_current_thread(async move {
let mut errors = vec![];
let ret_val = bundles
.format_messages(&keys, &mut errors)
.await
.into_iter()
.map(|msg| {
if let Some(msg) = msg {
OptionalL10nMessage {
is_present: true,
message: msg.into(),
}
} else {
OptionalL10nMessage {
is_present: false,
message: L10nMessage::default(),
}
}
})
.collect::<ThinVec<_>>();
assert_eq!(keys.len(), ret_val.len());
let errors = errors
.into_iter()
.map(|err| err.to_string().into())
.collect();
callback(&strong_promise, Some(&ret_val), &errors);
})
.expect("Failed to spawn future");
}
}
#[no_mangle]
pub extern "C" fn localization_new(
res_ids: &ThinVec<nsCString>,
is_sync: bool,
result: &mut *const LocalizationRc,
) -> nsresult {
*result = std::ptr::null();
let reg = get_l10n_registry();
let res_ids: Vec<String> = res_ids.iter().map(|res| res.to_string()).collect();
*result = RefPtr::forget_into_raw(LocalizationRc::new(&reg, res_ids, is_sync));
NS_OK
}
#[no_mangle]
pub unsafe extern "C" fn localization_addref(loc: &LocalizationRc) -> nsrefcnt {
loc.refcnt.inc()
}
#[no_mangle]
pub unsafe extern "C" fn localization_release(loc: *const LocalizationRc) -> nsrefcnt {
let rc = (*loc).refcnt.dec();
if rc == 0 {
Box::from_raw(loc as *const _ as *mut LocalizationRc);
}
rc
}
#[no_mangle]
pub extern "C" fn localization_add_res_id(loc: &LocalizationRc, res_id: &nsACString) {
let res_id = res_id.to_string();
loc.add_resource_id(res_id);
}
#[no_mangle]
pub extern "C" fn localization_add_res_ids(loc: &LocalizationRc, res_ids: &ThinVec<nsCString>) {
let res_ids = res_ids.iter().map(|s| s.to_string()).collect();
loc.add_resource_ids(res_ids);
}
#[no_mangle]
pub extern "C" fn localization_remove_res_id(loc: &LocalizationRc, res_id: &nsACString) -> usize {
let res_id = res_id.to_string();
loc.remove_resource_id(res_id)
}
#[no_mangle]
pub extern "C" fn localization_remove_res_ids(
loc: &LocalizationRc,
res_ids: &ThinVec<nsCString>,
) -> usize {
let res_ids = res_ids.iter().map(|s| s.to_string()).collect();
loc.remove_resource_ids(res_ids)
}
#[no_mangle]
pub extern "C" fn localization_format_value_sync(
loc: &LocalizationRc,
id: &nsACString,
args: &ThinVec<L10nArg>,
ret_val: &mut nsACString,
ret_err: &mut ThinVec<nsCString>,
) -> bool {
loc.format_value_sync(id, args, ret_val, ret_err)
}
#[no_mangle]
pub extern "C" fn localization_format_values_sync(
loc: &LocalizationRc,
keys: &ThinVec<L10nKey>,
ret_val: &mut ThinVec<nsCString>,
ret_err: &mut ThinVec<nsCString>,
) -> bool {
loc.format_values_sync(keys, ret_val, ret_err)
}
#[no_mangle]
pub extern "C" fn localization_format_messages_sync(
loc: &LocalizationRc,
keys: &ThinVec<L10nKey>,
ret_val: &mut ThinVec<OptionalL10nMessage>,
ret_err: &mut ThinVec<nsCString>,
) -> bool {
loc.format_messages_sync(keys, ret_val, ret_err)
}
#[no_mangle]
pub extern "C" fn localization_format_value(
loc: &LocalizationRc,
id: &nsACString,
args: &ThinVec<L10nArg>,
promise: &xpcom::Promise,
callback: extern "C" fn(&xpcom::Promise, &nsACString, &ThinVec<nsCString>),
) {
loc.format_value(id, args, promise, callback);
}
#[no_mangle]
pub extern "C" fn localization_format_values(
loc: &LocalizationRc,
keys: &ThinVec<L10nKey>,
promise: &xpcom::Promise,
callback: extern "C" fn(&xpcom::Promise, Option<&ThinVec<nsCString>>),
) {
loc.format_values(keys, promise, callback);
}
#[no_mangle]
pub extern "C" fn localization_format_messages(
loc: &LocalizationRc,
keys: &ThinVec<L10nKey>,
promise: &xpcom::Promise,
callback: extern "C" fn(
&xpcom::Promise,
Option<&ThinVec<OptionalL10nMessage>>,
&ThinVec<nsCString>,
),
) {
loc.format_messages(keys, promise, callback);
}
#[no_mangle]
pub extern "C" fn localization_upgrade(loc: &LocalizationRc) {
loc.upgrade();
}

View File

@ -67,6 +67,7 @@ fluent-ffi = { path = "../../../../intl/l10n/rust/fluent-ffi" }
l10nregistry-ffi = { path = "../../../../intl/l10n/rust/l10nregistry-ffi" }
l10nregistry = { git = "https://github.com/mozilla/l10nregistry-rs", rev = "55bf7f826d773303a67d8d7fdab099a04322d4fb" }
fluent-fallback = "0.5"
localization-ffi = { path = "../../../../intl/l10n/rust/localization-ffi" }
processtools = { path = "../../../components/processtools" }
qcms = { path = "../../../../gfx/qcms", features = ["c_bindings", "neon"], default-features = false }

View File

@ -29,7 +29,6 @@ extern crate gkrust_utils;
extern crate http_sfv;
extern crate jsrust_shared;
extern crate kvstore;
extern crate l10nregistry_ffi;
extern crate mapped_hyph;
extern crate mozurl;
extern crate mp4parse_capi;
@ -75,7 +74,8 @@ extern crate fluent;
extern crate fluent_ffi;
extern crate fluent_fallback;
extern crate l10nregistry;
extern crate l10nregistry_ffi;
extern crate localization_ffi;
#[cfg(not(target_os = "android"))]
extern crate viaduct;