Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WASM Demo #4684

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion tutorials/js.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ _We are still working on improving the user experience of using ICU4X from other

Similar to C++, the JS APIs mirror the Rust code in the `icu_capi` crate, which can be explored on [docs.rs][rust-docs], though the precise types used may be different.

See [`ffi/npm/examples/wasm-demo`] for an NPM package that uses the ICU4X package. You can run it using `npm run start`.
See [`docs/tutorials/npm`] for an NPM package that uses the ICU4X package. You can run it using `npm run start`.

We hope to fill in these docs over time with more examples.

Expand Down
1 change: 1 addition & 0 deletions tutorials/npm/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
dist/*
gen_hash.txt
135 changes: 135 additions & 0 deletions tutorials/npm/gen.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/bin/bash

locales=("ja" "th" "zh" "bn" "und" "de" "en")

if [ ! -d "dist" ]; then
mkdir dist
echo "dist folder created."
fi

for locale in "${locales[@]}"; do
cargo run --package icu_datagen -- --keys \
"calendar/japanese@1" \
"calendar/japanext@1" \
"datetime/buddhist/datelengths@1" \
"datetime/buddhist/datesymbols@1" \
"datetime/chinese/datelengths@1" \
"datetime/chinese/datesymbols@1" \
"datetime/coptic/datelengths@1" \
"datetime/coptic/datesymbols@1" \
"datetime/dangi/datelengths@1" \
"datetime/dangi/datesymbols@1" \
"datetime/ethiopic/datelengths@1" \
"datetime/ethiopic/datesymbols@1" \
"datetime/gregory/datelengths@1" \
"datetime/gregory/datesymbols@1" \
"datetime/hebrew/datelengths@1" \
"datetime/hebrew/datesymbols@1" \
"datetime/indian/datelengths@1" \
"datetime/indian/datesymbols@1" \
"datetime/islamic/datelengths@1" \
"datetime/islamic/datesymbols@1" \
"datetime/japanese/datelengths@1" \
"datetime/japanese/datesymbols@1" \
"datetime/japanext/datelengths@1" \
"datetime/japanext/datesymbols@1" \
"datetime/patterns/buddhist/date@1" \
"datetime/patterns/chinese/date@1" \
"datetime/patterns/coptic/date@1" \
"datetime/patterns/dangi/date@1" \
"datetime/patterns/datetime@1" \
"datetime/patterns/ethiopic/date@1" \
"datetime/patterns/gregory/date@1" \
"datetime/patterns/hebrew/date@1" \
"datetime/patterns/indian/date@1" \
"datetime/patterns/islamic/date@1" \
"datetime/patterns/japanese/date@1" \
"datetime/patterns/japanext/date@1" \
"datetime/patterns/persian/date@1" \
"datetime/patterns/roc/date@1" \
"datetime/patterns/time@1" \
"datetime/persian/datelengths@1" \
"datetime/persian/datesymbols@1" \
"datetime/roc/datelengths@1" \
"datetime/roc/datesymbols@1" \
"datetime/skeletons@1" \
"datetime/symbols/buddhist/months@1" \
"datetime/symbols/buddhist/years@1" \
"datetime/symbols/chinese/months@1" \
"datetime/symbols/chinese/years@1" \
"datetime/symbols/coptic/months@1" \
"datetime/symbols/coptic/years@1" \
"datetime/symbols/dangi/months@1" \
"datetime/symbols/dangi/years@1" \
"datetime/symbols/dayperiods@1" \
"datetime/symbols/ethiopic/months@1" \
"datetime/symbols/ethiopic/years@1" \
"datetime/symbols/gregory/months@1" \
"datetime/symbols/gregory/years@1" \
"datetime/symbols/hebrew/months@1" \
"datetime/symbols/hebrew/years@1" \
"datetime/symbols/indian/months@1" \
"datetime/symbols/indian/years@1" \
"datetime/symbols/islamic/months@1" \
"datetime/symbols/islamic/years@1" \
"datetime/symbols/japanese/months@1" \
"datetime/symbols/japanese/years@1" \
"datetime/symbols/japanext/months@1" \
"datetime/symbols/japanext/years@1" \
"datetime/symbols/persian/months@1" \
"datetime/symbols/persian/years@1" \
"datetime/symbols/roc/months@1" \
"datetime/symbols/roc/years@1" \
"datetime/symbols/weekdays@1" \
"datetime/timelengths@1" \
"datetime/timesymbols@1" \
"datetime/week_data@1" \
"decimal/symbols@1" \
"fallback/likelysubtags@1" \
"fallback/parents@1" \
"fallback/supplement/co@1" \
"list/and@1" \
"list/or@1" \
"list/unit@1" \
"locid_transform/aliases@1" \
"locid_transform/likelysubtags@1" \
"locid_transform/likelysubtags_ext@1" \
"locid_transform/likelysubtags_l@1" \
"locid_transform/likelysubtags_sr@1" \
"locid_transform/script_dir@1" \
"plurals/cardinal@1" \
"plurals/ordinal@1" \
"plurals/ranges@1" \
"segmenter/dictionary/w_auto@1" \
"segmenter/dictionary/wl_ext@1" \
"segmenter/grapheme@1" \
"segmenter/line@1" \
"segmenter/lstm/wl_auto@1" \
"segmenter/sentence@1" \
"segmenter/word@1" \
"time_zone/bcp47_to_iana@1" \
"time_zone/exemplar_cities@1" \
"time_zone/formats@1" \
"time_zone/generic_long@1" \
"time_zone/generic_short@1" \
"time_zone/iana_to_bcp47@1" \
"time_zone/metazone_period@1" \
"time_zone/specific_long@1" \
"time_zone/specific_short@1" \
--fallback preresolved --locales $locale --format blob2 --out dist/$locale.postcard --overwrite
done

ts_content="const locales: string[] = ["

for locale in "${locales[@]}"; do
ts_content+="\"$locale\", "
done

ts_content=${ts_content%, }
ts_content+="];"

ts_content+="\nexport default locales;"

echo "$ts_content" > dist/locales.ts

echo "locales.ts file has been generated."
47 changes: 47 additions & 0 deletions tutorials/npm/gen_helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import fs from 'fs';
import crypto from 'crypto';
import { spawn } from 'child_process';

const genShFile = 'gen.sh';
const hashFile = 'gen_hash.txt';

function calculateHash(filePath) {
const hash = crypto.createHash('sha256');
const fileData = fs.readFileSync(filePath);
hash.update(fileData);
return hash.digest('hex');
}

function runGenSh() {
console.log('Running gen.sh...');
const genShProcess = spawn('sh', [genShFile]);

genShProcess.stdout.on('data', (data) => {
console.log(data.toString());
});

genShProcess.stderr.on('data', (data) => {
console.error(data.toString());
});

genShProcess.on('close', (code) => {
if (code !== 0) {
console.error(`gen.sh exited with code ${code}`);
}
});
}

try {
const currentHash = calculateHash(genShFile);
const previousHash = fs.existsSync(hashFile) ? fs.readFileSync(hashFile, 'utf-8') : null;

if (currentHash !== previousHash) {
runGenSh();
fs.writeFileSync(hashFile, currentHash);
console.log('gen.sh has been updated. Output regenerated.');
} else {
console.log('gen.sh has not changed. Skipping execution.');
}
} catch (error) {
console.error('An error occurred:', error);
}
2 changes: 1 addition & 1 deletion tutorials/npm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"clean": "rm dist/*",
"build": "webpack",
"build": "node gen_helper.js && webpack",
"start": "webpack serve --mode development --port 12349",
"tsc": "tsc -p ."
},
Expand Down
12 changes: 7 additions & 5 deletions tutorials/npm/src/ts/app.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ICU4XDataProvider } from 'icu4x';
import { DataProviderManager } from './data-provider-manager';
import * as fdf from './fixed-decimal';
import * as dtf from './date-time';
import * as seg from './segmenter';
Expand All @@ -8,9 +8,11 @@ import 'bootstrap/js/dist/dropdown';
import 'bootstrap/js/dist/collapse';

(async function init() {
const dataProvider = ICU4XDataProvider.create_compiled();
fdf.setup(dataProvider);
dtf.setup(dataProvider);
seg.setup(dataProvider);
const dataManager = await DataProviderManager.create();

fdf.setup(dataManager);
dtf.setup(dataManager);
seg.setup(dataManager);

(document.querySelector("#bigspinner") as HTMLElement).style.display = "none";
})()
87 changes: 87 additions & 0 deletions tutorials/npm/src/ts/data-provider-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
ICU4XDataProvider,
ICU4XLocale,
ICU4XLocaleFallbacker,
} from 'icu4x';

export class DataProviderManager {

private dataProvider: ICU4XDataProvider;
private loadedLocales: Set<ICU4XLocale>;

private constructor() {
this.loadedLocales = new Set<ICU4XLocale>();
}

public static async create(): Promise<DataProviderManager> {
const manager = new DataProviderManager();
await manager.init();
return manager;
}

private async init() {

const enFilePath = 'dist/en.postcard';
let enProvider = await this.createDataProviderFromBlob(enFilePath);
this.loadedLocales.add(ICU4XLocale.create_from_string("en"));
const unFilePath = 'dist/en.postcard';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried this with dist/und.postcard, with no help. Will fix this in consecutive commits**

let unProvider = await this.createDataProviderFromBlob(unFilePath);
let fallbacker = ICU4XLocaleFallbacker.create(unProvider);
enProvider.enable_locale_fallback_with(fallbacker);
this.dataProvider = enProvider;
}

private async createDataProviderFromBlob(filePath: string): Promise<ICU4XDataProvider> {
const blob = await this.readBlobFromFile(filePath);
const arrayBuffer = await blob.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
const newDataProvider = ICU4XDataProvider.create_from_byte_slice(uint8Array);
return newDataProvider;
}

private async readBlobFromFile(path: string): Promise<Blob> {
const response = await fetch(path);
if (!response.ok) {
throw new Error(`Failed to fetch file: ${response.statusText}`);
}
const blob = await response.blob();
return blob;
}

public supportsLocale(locid: string): boolean {
const locales = this.getLoadedLocales();
const localesFinal: string[] = [];
locales.forEach((item: ICU4XLocale) => {
localesFinal.push(item.to_string)
})
const loadedLocales = new Set(localesFinal);
return loadedLocales.has(locid);
}

public async loadLocale(newLocale: string): Promise<ICU4XDataProvider> {
const icu4xLocale = ICU4XLocale.create_from_string(newLocale);
const newFilePath = `dist/${newLocale}.postcard`;
let newProvider = await this.createDataProviderFromBlob(newFilePath);
await this.dataProvider.fork_by_locale(newProvider);
this.loadedLocales.add(ICU4XLocale.create_from_string(newLocale));
return this.dataProvider;
}

public async getSegmenterProviderLocale(): Promise<ICU4XDataProvider> {
const segmenterLocale = ['ja', 'zh', 'th'];
let segmenterProvider: ICU4XDataProvider;
for (let i = 0; i < segmenterLocale.length; i++) {
segmenterProvider = await this.loadLocale(segmenterLocale[i]);
}
return segmenterProvider;
}

public getLoadedLocales() {
return this.loadedLocales;
}

public getDataProvider(): ICU4XDataProvider {
return this.dataProvider;
}

}
43 changes: 34 additions & 9 deletions tutorials/npm/src/ts/date-time.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
import { ICU4XDataProvider, ICU4XDateLength, ICU4XDateTime, ICU4XDateTimeFormatter, ICU4XLocale, ICU4XTimeLength, ICU4XCalendar } from "icu4x";
import { Ok, Result, result, unwrap } from "./index";
import {
ICU4XDataProvider,
ICU4XDateLength,
ICU4XDateTime,
ICU4XDateTimeFormatter,
ICU4XLocale,
ICU4XTimeLength,
ICU4XCalendar
} from "icu4x";
import { DataProviderManager } from './data-provider-manager';
import {
Ok,
Result,
result,
unwrap
} from "./index";

export class DateTimeDemo {
#displayFn: (formatted: string) => void;
#dataProvider: ICU4XDataProvider;
#dataProviderManager: DataProviderManager;

#localeStr: string;
#calendarStr: string;
Expand All @@ -16,12 +31,13 @@ export class DateTimeDemo {
#formatter: Result<ICU4XDateTimeFormatter>;
#dateTime: Result<ICU4XDateTime> | null;

constructor(displayFn: (formatted: string) => void, dataProvider: ICU4XDataProvider) {
constructor(displayFn: (formatted: string) => void, dataProviderManager: DataProviderManager) {

this.#displayFn = displayFn;
this.#dataProvider = dataProvider;

this.#dataProvider = dataProviderManager.getDataProvider();
this.#dataProviderManager = dataProviderManager;
this.#locale = Ok(ICU4XLocale.create_from_string("en"));
this.#calendar = Ok(ICU4XCalendar.create_for_locale(dataProvider, unwrap(this.#locale)));
this.#calendar = Ok(ICU4XCalendar.create_for_locale(this.#dataProvider, unwrap(this.#locale)));
this.#dateLength = ICU4XDateLength.Short;
this.#timeLength = ICU4XTimeLength.Short;
this.#dateTime = null;
Expand All @@ -37,9 +53,18 @@ export class DateTimeDemo {
this.#updateFormatter();
}

setLocale(locid: string): void {
this.#localeStr = locid;
this.#updateLocaleAndCalendar();
async setLocale(locid: string): Promise <void> {
this.#locale = result(() => ICU4XLocale.create_from_string(locid));
if (this.#locale.ok == true) {
if(!this.#dataProviderManager.supportsLocale(locid)){
await this.updateProvider(this.#locale.value);
}
}
this.#updateFormatter()
}

async updateProvider(newLocale: ICU4XLocale) {
await this.#dataProviderManager.loadLocale(newLocale.to_string());
this.#updateFormatter();
}

Expand Down
Loading
Loading