From ce6f6a3c0b18a3ffb8e693ac7a9ebec8677ecaee Mon Sep 17 00:00:00 2001 From: James M Snell Date: Thu, 15 Oct 2015 21:48:09 -0700 Subject: [PATCH] src: initial localization support This is a work in progress effort to introduce the *optional* ability to localize node's own outputs. Currently, Node (and V8) outputs english only error, debug and help output. This is a step towards allowing translated versions of node to be built. Currently, this is hidden behind the --with-intl=full-icu switch. This is because there are certain capabilities of ICU that are only enabled with the full data set. This is a work in progress that SHOULD NOT BE LANDED in master yet. --- Makefile | 1 + deps/l10n/README.md | 43 ++++++++++++ deps/l10n/icures.py | 129 ++++++++++++++++++++++++++++++++++ deps/l10n/include/l10n.h | 35 +++++++++ deps/l10n/l10n.gyp | 123 ++++++++++++++++++++++++++++++++ deps/l10n/resources/en.txt | Bin 0 -> 72 bytes deps/l10n/resources/en_US.txt | Bin 0 -> 18 bytes deps/l10n/resources/root.txt | Bin 0 -> 114 bytes deps/l10n/src/l10n.cc | 50 +++++++++++++ lib/internal/l10n.js | 17 +++++ node.gyp | 10 ++- src/node.cc | 64 ++++++++++++----- src/node_l10n.cc | 47 +++++++++++++ src/node_l10n.h | 76 ++++++++++++++++++++ test/parallel/test-l10n.js | 21 ++++++ tools/icu/icu-generic.gyp | 37 ++++++++-- 16 files changed, 631 insertions(+), 22 deletions(-) create mode 100644 deps/l10n/README.md create mode 100644 deps/l10n/icures.py create mode 100644 deps/l10n/include/l10n.h create mode 100644 deps/l10n/l10n.gyp create mode 100644 deps/l10n/resources/en.txt create mode 100644 deps/l10n/resources/en_US.txt create mode 100644 deps/l10n/resources/root.txt create mode 100644 deps/l10n/src/l10n.cc create mode 100644 lib/internal/l10n.js create mode 100644 src/node_l10n.cc create mode 100644 src/node_l10n.h create mode 100644 test/parallel/test-l10n.js diff --git a/Makefile b/Makefile index 02619fac98ddd7..902a66ba818074 100644 --- a/Makefile +++ b/Makefile @@ -70,6 +70,7 @@ clean: @if [ -d out ]; then find out/ -name '*.o' -o -name '*.a' -o -name '*.d' | xargs rm -rf; fi -rm -rf node_modules @if [ -d deps/icu ]; then echo deleting deps/icu; rm -rf deps/icu; fi + -rm -rf out/Release/obj/gen/noderestmp -rm -f test.tap distclean: diff --git a/deps/l10n/README.md b/deps/l10n/README.md new file mode 100644 index 00000000000000..0d0c3910cfb08b --- /dev/null +++ b/deps/l10n/README.md @@ -0,0 +1,43 @@ +Localization bundle for Node. + +This is pretty straightforward... if ICU is present and --with-intl-full-icu, +then ICU's resource bundle mechanism is used, the resources are compiled +statically into the library, which can then be used within Node. If ICU is not +present, a simple printf fallback is used. + +``` +./configure --with-intl=full-icu +make +``` +When the --with-intl=full-icu switch is on, the resources are compiled into a +static library that is statically linked into Node. The next step will be to +make it so that additional bundles can be specified at runtime. + +Resource bundles are located in the resources directory. Standard ICU bundle +format but keep it simple, we currently only support top level resources. + +Within the C/C++ code, use the macros: + +```cc +#include "node_l10n.h" + +L10N_PRINT("TEST", "This is the fallback"); +L10N_PRINTF("TEST2", "Testing %s %d", "a", 1); +``` + +In the JS code, use the internal/l10n.js +```javascript +const l10n = require('internal/l10n'); +console.log(l10n("TEST", "This is the fallback")); +console.log(l10n("TEST2", "Fallback %s %d", "a", 1)); +``` + +Use the `--icu-data-dir` switch to specify a location containing alternative +node.dat files containing alternative translations. Note that this is the +same switch used to specify alternative ICU common data files. + +One approach that ought to work here is publishing translation node.dat files +to npm. Then, the developer does a `npm install node_dat_de` (for instance) +to grab the appropriate translations. Then, then can start node like: + +`node --icu-data-dir=node_modules/node_dat_de` and have things just work. diff --git a/deps/l10n/icures.py b/deps/l10n/icures.py new file mode 100644 index 00000000000000..b6d11879541653 --- /dev/null +++ b/deps/l10n/icures.py @@ -0,0 +1,129 @@ +#!/usr/bin/python + +import sys +import shutil +reload(sys) +sys.setdefaultencoding("utf-8") + +import optparse +import os +import glob + +endian=sys.byteorder + +parser = optparse.OptionParser(usage="usage: %prog -n {NAME} -d {DEST} -i {ICU}") + +parser.add_option("-d", "--dest-dir", + action="store", + dest="dest", + help="The destination directory") + +parser.add_option("-n", "--name", + action="store", + dest="name", + help="The application package name") + +parser.add_option("-i", "--icu-path", + action="store", + dest="icu", + help="The ICU tool path") + +parser.add_option("-l", "--endian", + action="store", + dest="endian", + help='endian: big, little or host. your default is "%s"' % endian, default=endian, metavar='endianess') + +(options, args) = parser.parse_args() + +optVars = vars(options); + +for opt in ["dest", "name", "icu"]: + if optVars[opt] is None: + parser.error("Missing required option: %s" % opt) + sys.exit(1) + +if options.endian not in ("big", "little", "host"): + parser.error("Unknown endianess: %s" % options.endian) + sys.exit(1) + +if options.endian == "host": + options.endian = endian + +if not os.path.isdir(options.dest): + parser.error("Destination is not a directory") + sys.exit(1) + +if options.icu[-1] is '"': + options.icu = options.icu[:-1] + +if not os.path.isdir(options.icu): + parser.error("ICU Path is not a directory") + sys.exit(1) + +if options.icu[-1] != os.path.sep: + options.icu += os.path.sep + +genrb = options.icu + 'genrb' +icupkg = options.icu + 'icupkg' + +if sys.platform.startswith('win32'): + genrb += '.exe' + icupkg += '.exe' + +if not os.path.isfile(genrb): + parser.error('ICU Tool "%s" does not exist' % genrb) + sys.exit(1) + +if not os.path.isfile(icupkg): + parser.error('ICU Tool "%s" does not exist' % icupkg) + sys.exit(1) + +def runcmd(tool, cmd, doContinue=False): + cmd = "%s %s" % (tool, cmd) + rc = os.system(cmd) + if rc is not 0 and not doContinue: + print "FAILED: %s" % cmd + sys.exit(1) + return rc + +resfiles = glob.glob("%s%s*.res" % (options.dest, os.path.sep)) +_listfile = os.path.join(options.dest, 'packagefile.lst') +datfile = "%s%s%s.dat" % (options.dest, os.path.sep, options.name) + +def clean(): + for f in resfiles: + if os.path.isfile(f): + os.remove(f) + if os.path.isfile(_listfile): + os.remove(_listfile) + if (os.path.isfile(datfile)): + os.remove(datfile) + +## Step 0, Clean up from previous build +clean() + +## Step 1, compile the txt files in res files + +if sys.platform.startswith('win32'): + srcfiles = glob.glob('resources/*.txt') + runcmd(genrb, "-e utf16 -d %s %s" % (options.dest, " ".join(srcfiles))) +else: + runcmd(genrb, "-e utf16 -d %s resources%s*.txt" % (options.dest, os.path.sep)) + +resfiles = [os.path.relpath(f) for f in glob.glob("%s%s*.res" % (options.dest, os.path.sep))] + +# pkgdata requires relative paths... it's annoying but necessary +# for us to change into the dest directory to work +cwd = os.getcwd(); +os.chdir(options.dest) + +## Step 2, generate the package list +listfile = open(_listfile, 'w') +listfile.write(" ".join([os.path.basename(f) for f in resfiles])) +listfile.close() + +## Step 3, generate the dat file using icupkg and the package list +runcmd(icupkg, '-a packagefile.lst new %s.dat' % options.name); + +## All done with this tool at this point... +os.chdir(cwd); # go back to original working directory diff --git a/deps/l10n/include/l10n.h b/deps/l10n/include/l10n.h new file mode 100644 index 00000000000000..3aec8baad21202 --- /dev/null +++ b/deps/l10n/include/l10n.h @@ -0,0 +1,35 @@ +#ifndef L10N__H +#define L10N__H + +#include +#include +#include + +#ifdef _WIN32 +# define L10N_EXTERN /* nothing */ +#elif __GNUC__ >= 4 +# define L10N_EXTERN __attribute__((visibility("default"))) +#else +# define L10N_EXTERN /* nothing */ +#endif + +/** + * Initialize the resource bundle. This will register an atexit handler + * to deal with the cleanup in normal termination + **/ +L10N_EXTERN bool l10n_initialize(const char * locale, const char * icu_data_dir); + +/** + * Lookup the key, return the value. Doesn't get much easier. If the key + * is not found in the bundle, fallback is returned instead. The caller + * owns the string and must delete[] it when done lest horrible things. + **/ +L10N_EXTERN const uint16_t * l10n_fetch(const char * key, + const uint16_t * fallback); + +#define L10N(key, fallback) l10n_fetch(key, fallback) +#define L10N_LOCALE() uloc_getDefault() +#define L10N_INIT(locale, icu_data_dir) \ + do {l10n_initialize(locale, icu_data_dir);} while(0) + +#endif // L10N__H diff --git a/deps/l10n/l10n.gyp b/deps/l10n/l10n.gyp new file mode 100644 index 00000000000000..2bd4efe56521cb --- /dev/null +++ b/deps/l10n/l10n.gyp @@ -0,0 +1,123 @@ +{ + 'includes': [ '../../icu_config.gypi' ], + 'targets': [ + { + 'target_name': 'noderes', + 'type': '<(library)', + 'conditions': [ + [ + 'v8_enable_i18n_support==1', + { + 'include_dirs': [ 'include', 'src' ], + 'direct_dependent_settings': { + 'include_dirs': [ 'include' ] + }, + 'sources': [ + 'include/l10n.h', + 'include/l10n_version.h', + 'src/l10n.cc' + ], + 'dependencies': [ + '<(icu_gyp_path):icui18n', + '<(icu_gyp_path):icuuc', + 'icu_noderes_data' + ] + } + ] + ] + }, + { + #### build the resource bundle using ICU's tools #### + 'target_name': 'icu_noderes_data', + 'type': '<(library)', + 'conditions': [ + [ + 'v8_enable_i18n_support==1', + { + 'toolsets': [ 'target' ], + 'dependencies': [ + '<(icu_gyp_path):icu_implementation#host', + '<(icu_gyp_path):icu_uconfig', + '<(icu_gyp_path):genrb#host', + '<(icu_gyp_path):genccode#host', + '<(icu_gyp_path):icupkg#host' + ], + 'actions': [ + { + 'action_name': 'icures', + 'inputs': [ + 'resources/root.txt', + 'resources/en.txt', + 'resources/en_US.txt' + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/noderestmp/node.dat' + ], + 'action': [ + 'python', + 'icures.py', + '-n', 'node', + '-d', '<(SHARED_INTERMEDIATE_DIR)/noderestmp', + '-i', '<(PRODUCT_DIR)' + ] + }, + { + 'action_name': 'icugen', + 'inputs': [ + '<(SHARED_INTERMEDIATE_DIR)/noderestmp/node.dat' + ], + 'conditions': [ + [ + 'OS != "win"', + { + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/node_dat.c' + ], + 'action': [ + '<(PRODUCT_DIR)/genccode', + '-e', 'node', + '-d', '<(SHARED_INTERMEDIATE_DIR)', + '-f', 'node_dat', + '<@(_inputs)' + ] + }, + { + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/node_dat.obj' + ], + 'action': [ + '<(PRODUCT_DIR)/genccode', + '-o', + '-d', '<(SHARED_INTERMEDIATE_DIR)', + '-n', 'node', + '-e', 'node', + '<@(_inputs)' + ] + } + ] + ] + } + ], + 'conditions': [ + [ 'OS == "win"', + { + 'sources': [ + '<(SHARED_INTERMEDIATE_DIR)/node_dat.obj' + ] + }, + { + 'include_dirs': [ + '../icu/source/common' + ], + 'sources': [ + '<(SHARED_INTERMEDIATE_DIR)/node_dat.c' + ] + } + ] + ] + } + ] + ] + } + ] +} diff --git a/deps/l10n/resources/en.txt b/deps/l10n/resources/en.txt new file mode 100644 index 0000000000000000000000000000000000000000..72c7e6b751ca6bb3041f189d899a90c7b79816ac GIT binary patch literal 72 zcmZQbWyoVtV5nx`0+I?0Aq=hz!9WruqQIa8GvqL2G88jpfaOzxtP&s}B(4M$ JsRil;VgP_n3hn>^ literal 0 HcmV?d00001 diff --git a/deps/l10n/resources/en_US.txt b/deps/l10n/resources/en_US.txt new file mode 100644 index 0000000000000000000000000000000000000000..d5eb3e37eda9a2e4937d521a6c03289140c1cfe9 GIT binary patch literal 18 ZcmZQbWyoWQX9#5oW>8?LW~gQ00stLm0{s90 literal 0 HcmV?d00001 diff --git a/deps/l10n/resources/root.txt b/deps/l10n/resources/root.txt new file mode 100644 index 0000000000000000000000000000000000000000..1ecda4fd1c34ba8959c072159a71ffcbef8b7698 GIT binary patch literal 114 zcmZQ5V#o)=5(Wi^Y6dPKslX7z;K~pTBtaqy3`z_^4E{g};=^Q=fb3c{)kav +#include +#include +#include "l10n.h" + +// The ICU bundle name +#define L10N_APPDATA "node" + +extern "C" const char U_DATA_API node_dat[]; + +UResourceBundle *bundle; + +void l10n_cleanup() { + ures_close(bundle); +} + +bool l10n_initialize(const char * locale, + const char * icu_data_dir) { + UErrorCode status = U_ZERO_ERROR; + if (!icu_data_dir) { + udata_setAppData("node", &node_dat, &status); + } + if (U_FAILURE(status)) { + return FALSE; + } else { + bundle = ures_open(L10N_APPDATA, locale, &status); + if (U_SUCCESS(status)) { + atexit(l10n_cleanup); + return TRUE; + } else { + return FALSE; + } + } +} + +const uint16_t * l10n_fetch(const char * key, + const uint16_t * fallback) { + UErrorCode status = U_ZERO_ERROR; + int32_t len = 0; + const UChar* res = ures_getStringByKey( + bundle, + key, + &len, + &status); + const uint16_t* ret = static_cast(res); + if (U_SUCCESS(status)) { + return ret; + } + return fallback; +} diff --git a/lib/internal/l10n.js b/lib/internal/l10n.js new file mode 100644 index 00000000000000..f8ffbf402eab24 --- /dev/null +++ b/lib/internal/l10n.js @@ -0,0 +1,17 @@ +'use strict'; +const l10n = process.binding('l10n'); +const util = require("util"); + +module.exports = function(key, fallback) { + // currently this works like util.format, but the plan + // is to evolve it into a template string tag. + var args = Array.prototype.slice.call(arguments,2); + return key + ': ' + util.format.apply(this, args); +}; + +Object.defineProperty(module.exports, 'locale', { + value: l10n.locale +}); +Object.defineProperty(module.exports, 'icu', { + value: l10n.icu +}); diff --git a/node.gyp b/node.gyp index b35c8a508a7589..6e67ae556e4ab5 100644 --- a/node.gyp +++ b/node.gyp @@ -76,6 +76,7 @@ 'lib/internal/socket_list.js', 'lib/internal/util.js', 'lib/internal/streams/lazy_transform.js', + 'lib/internal/l10n.js' ], }, @@ -108,6 +109,7 @@ 'src/handle_wrap.cc', 'src/js_stream.cc', 'src/node.cc', + 'src/node_l10n.c', 'src/node_buffer.cc', 'src/node_constants.cc', 'src/node_contextify.cc', @@ -135,6 +137,7 @@ 'src/udp_wrap.cc', 'src/uv.cc', # headers to make for a more pleasant IDE experience + 'src/node_l10n.h', 'src/async-wrap.h', 'src/async-wrap-inl.h', 'src/base-object.h', @@ -212,9 +215,14 @@ 'defines': [ 'NODE_HAVE_I18N_SUPPORT=1' ], 'dependencies': [ '<(icu_gyp_path):icui18n', - '<(icu_gyp_path):icuuc', + '<(icu_gyp_path):icuuc' ], 'conditions': [ + [ 'icu_small=="false"', { + 'dependencies': [ + 'deps/l10n/l10n.gyp:noderes' + ] + }], [ 'icu_small=="true"', { 'defines': [ 'NODE_HAVE_SMALL_ICU=1' ], }]], diff --git a/src/node.cc b/src/node.cc index 1dfa854ed5bb99..06a400fceb1388 100644 --- a/src/node.cc +++ b/src/node.cc @@ -42,6 +42,7 @@ #include "v8-debug.h" #include "v8-profiler.h" #include "zlib.h" +#include "node_l10n.h" #include #include // PATH_MAX @@ -3213,6 +3214,8 @@ static void ParseArgs(int* argc, new_v8_argv[0] = argv[0]; new_argv[0] = argv[0]; + bool print_help = FALSE, eval_fail = FALSE; + unsigned int index = 1; while (index < nargs && argv[index][0] == '-') { const char* const arg = argv[index]; @@ -3224,8 +3227,10 @@ static void ParseArgs(int* argc, printf("%s\n", NODE_VERSION); exit(0); } else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) { - PrintHelp(); - exit(0); + // PrintHelp(); + // exit(0); + print_help = TRUE; + break; } else if (strcmp(arg, "--eval") == 0 || strcmp(arg, "-e") == 0 || strcmp(arg, "--print") == 0 || @@ -3239,8 +3244,10 @@ static void ParseArgs(int* argc, args_consumed += 1; eval_string = argv[index + 1]; if (eval_string == nullptr) { - fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg); - exit(9); + // fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg); + // exit(9); + eval_fail = TRUE; + break; } } else if ((index + 1 < nargs) && argv[index + 1] != nullptr && @@ -3303,6 +3310,31 @@ static void ParseArgs(int* argc, index += args_consumed; } + // Move ICU Initialization here before we init the L10N stuff... so we + // can handle PrintHelp and arg errors also... + #if defined(NODE_HAVE_I18N_SUPPORT) + if (icu_data_dir == nullptr) { + // if the parameter isn't given, use the env variable. + icu_data_dir = secure_getenv("NODE_ICU_DATA"); + } + // Initialize ICU. + // If icu_data_dir is nullptr here, it will load the 'minimal' data. + if (!i18n::InitializeICUDirectory(icu_data_dir)) { + FatalError(nullptr, "Could not initialize ICU " + "(check NODE_ICU_DATA or --icu-data-dir parameters)"); + } + #endif + + L10N_INIT(nullptr, icu_data_dir); + if (print_help) { + PrintHelp(); + exit(0); + } + if (eval_fail) { + fprintf(stderr, "node: -e|--eval requires an argument\n"); + exit(9); + } + // Copy remaining arguments. const unsigned int args_left = nargs - index; memcpy(new_argv + new_argc, argv + index, args_left * sizeof(*argv)); @@ -3717,18 +3749,18 @@ void Init(int* argc, } #endif -#if defined(NODE_HAVE_I18N_SUPPORT) - if (icu_data_dir == nullptr) { - // if the parameter isn't given, use the env variable. - icu_data_dir = secure_getenv("NODE_ICU_DATA"); - } - // Initialize ICU. - // If icu_data_dir is nullptr here, it will load the 'minimal' data. - if (!i18n::InitializeICUDirectory(icu_data_dir)) { - FatalError(nullptr, "Could not initialize ICU " - "(check NODE_ICU_DATA or --icu-data-dir parameters)"); - } -#endif +// #if defined(NODE_HAVE_I18N_SUPPORT) +// if (icu_data_dir == nullptr) { +// // if the parameter isn't given, use the env variable. +// icu_data_dir = secure_getenv("NODE_ICU_DATA"); +// } +// // Initialize ICU. +// // If icu_data_dir is nullptr here, it will load the 'minimal' data. +// if (!i18n::InitializeICUDirectory(icu_data_dir)) { +// FatalError(nullptr, "Could not initialize ICU " +// "(check NODE_ICU_DATA or --icu-data-dir parameters)"); +// } +// #endif // The const_cast doesn't violate conceptual const-ness. V8 doesn't modify // the argv array or the elements it points to. if (v8_argc > 1) diff --git a/src/node_l10n.cc b/src/node_l10n.cc new file mode 100644 index 00000000000000..c0cbb2dd0d9281 --- /dev/null +++ b/src/node_l10n.cc @@ -0,0 +1,47 @@ +#include "node_l10n.h" + +#include "node_wrap.h" +#include + +namespace node { + + using v8::String; + using v8::Boolean; + using v8::HandleScope; + using v8::FunctionCallbackInfo; + using v8::Context; + using v8::Object; + using v8::Value; + using v8::Isolate; + using v8::Local; + +namespace l10n { + + void Initialize(Local target, + Local unused, + Local context) { + Environment* env = Environment::GetCurrent(context); + env->SetMethod(target, "fetch", Fetch); + #if defined(NODE_HAVE_I18N_SUPPORT) + target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "icu"), + Boolean::New(env->isolate(), 1)); + target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "locale"), + String::NewFromUtf8(env->isolate(), L10N_LOCALE())); + #endif + } + + void Fetch(const FunctionCallbackInfo& args) { + assert(args.Length() >= 2); + HandleScope handle_scope(args.GetIsolate()); + String::Utf8Value key(args[0]); + String::Value fallback(args[1]); + args.GetReturnValue().Set( + String::NewFromTwoByte( + args.GetIsolate(), + L10N(*key,*fallback))); + } + +} // namespace l10n +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_BUILTIN(l10n, node::l10n::Initialize); diff --git a/src/node_l10n.h b/src/node_l10n.h new file mode 100644 index 00000000000000..4cfa7105752d3b --- /dev/null +++ b/src/node_l10n.h @@ -0,0 +1,76 @@ +#ifndef SRC_NODE_L10N_H_ +#define SRC_NODE_L10N_H_ + +/** + * Defines a handful of macros: + * + * - L10N(key,fallback) + * - L10N_LOCALE() + * - L10N_PRINT(key, fallback) + * - L10N_PRINTF(key, fallback, ...) + * - L10N_INIT(locale, icu_data_dir) + * + * When --with-intl=full-icu, this will use the ICU message bundle + * mechanism. In all other cases, this uses the simple fallback. + * This will minimize impact for default builds but will allow + * node to be fully localized when using the full ICU dataset. + **/ + +#if defined(NODE_HAVE_I18N_SUPPORT) +#if !defined(NODE_HAVE_SMALL_ICU) + // only enabled with --with-intl=full-icu + #include + + #define L10N_PRINT(key, fallback) \ + do { \ + UChar *str; \ + UErrorCode status = U_ZERO_ERROR; \ + str=(UChar*)malloc(sizeof(UChar) * (strlen(fallback) +1)); \ + u_uastrcpy(str, fallback); \ + u_printf_u(L10N(key, str)); \ + free(str); \ + } while(0) + + #define L10N_PRINTF(key, fallback, ...) \ + do { \ + UChar *str; \ + UErrorCode status = U_ZERO_ERROR; \ + str=(UChar*)malloc(sizeof(UChar) * (strlen(fallback) +1)); \ + u_uastrcpy(str, fallback); \ + u_printf_u(L10N(key, str),__VA_ARGS__); \ + free(str); \ + } while(0) +#endif +#endif + +#ifndef L10N + // not using full-icu + #define L10N(key, fallback) fallback + #define L10N_LOCALE() "en_US" + #define L10N_INIT(locale, icu_data_dir) do {} while(0) + #define L10N_PRINT(key, fallback) printf(fallback) + #define L10N_PRINTF(key, fallback, ...) printf(fallback, __VA_ARGS__) +#endif + +#include "env.h" + +namespace node { + +using v8::Local; +using v8::Object; +using v8::Value; +using v8::Context; +using v8::FunctionCallbackInfo; + +namespace l10n { + + static void Initialize(Local target, + Local unused, + Local context); + + static void Fetch(const FunctionCallbackInfo& args); + +} // namespace l10n +} // namespace node + +#endif // SRC_NODE_L10N_H_ diff --git a/test/parallel/test-l10n.js b/test/parallel/test-l10n.js new file mode 100644 index 00000000000000..10a5fbd60b8258 --- /dev/null +++ b/test/parallel/test-l10n.js @@ -0,0 +1,21 @@ +'use strict'; + +// Flags: --expose-internals + +const common = require('../common'); +const l10n = require('internal/l10n'); +const assert = require('assert'); + +// right now we only support english + +if (l10n.icu) { +// ICU is present, the strings will be localized + assert.equal('English Testing', l10n('TEST', 'Foo')); + assert.equal('ROOT abc 1', l10n('TEST2', 'Foo', 'abc', 1)); + assert.equal('Fallback', l10n('TEST3', 'Fallback')); +} else { +// ICU is not present, the fallbacks will be used + assert.equal('Foo', l10n('TEST', 'Foo')); + assert.equal('Foo abc 1', l10n('TEST2', 'Foo', 'abc', 1)); + assert.equal('Fallback', l10n('TEST3', 'Fallback')); +} diff --git a/tools/icu/icu-generic.gyp b/tools/icu/icu-generic.gyp index 222a9e95664d50..fe67df1e032965 100644 --- a/tools/icu/icu-generic.gyp +++ b/tools/icu/icu-generic.gyp @@ -20,9 +20,16 @@ 'type': 'none', 'toolsets': [ 'target' ], 'direct_dependent_settings': { - 'defines': [ - 'UCONFIG_NO_CONVERSION=1', + 'conditions': [ + [ + 'icu_small == "true"', { + 'defines': [ 'UCONFIG_NO_CONVERSION=1' ] + } + ] ] + # 'defines': [ + # 'UCONFIG_NO_CONVERSION=1', + # ] }, }, { @@ -122,6 +129,17 @@ '<@(icu_src_i18n)' ], 'conditions': [ + ['icu_small == "false"', { + 'sources': [ + '<@(icu_src_io)' + ], + 'include_dirs': [ + '../../deps/icu/source/io' + ], + 'defines': [ + 'U_IO_IMPLEMENTATION=1' + ] + }], [ 'icu_ver_major == 55', { 'sources!': [ ## Strip out the following for ICU 55 only. ## add more conditions in the future? @@ -163,16 +181,25 @@ '../../deps/icu/source/i18n/uspoof_wsconf.h', ]}]], 'include_dirs': [ - '../../deps/icu/source/i18n', + '../../deps/icu/source/i18n' ], 'defines': [ - 'U_I18N_IMPLEMENTATION=1', + 'U_I18N_IMPLEMENTATION=1' ], 'dependencies': [ 'icuucx', 'icu_implementation', 'icu_uconfig', 'icu_uconfig_target' ], 'direct_dependent_settings': { 'include_dirs': [ - '../../deps/icu/source/i18n', + '../../deps/icu/source/i18n' ], + 'conditions': [ + [ + 'icu_small == "false"', { + 'include_dirs': [ + '../../deps/icu/source/io' + ] + } + ] + ] }, 'export_dependent_settings': [ 'icuucx', 'icu_uconfig_target' ], }],