1.1.1 • Published 2 years ago

world-speaker v1.1.1

Weekly downloads
-
License
ISC
Repository
-
Last release
2 years ago

World Speaker

Introduction

This is a vary simple and straight to use localization library, light weight and with no dependencies. Keeps things very clean and it's perfect for projects where localization is needed but it's not so much heavy or complex. I personally use World Speaker when I need to draw quick user interfaces putting very low effort in the localization.

This manual is going to show you method by method the usage of the library on JavaScript (but it's thinked in and for TypeScript). After the methods there is a little section relative to data format you may want to understand.

Finally, there is a last section of tips for newbies.

Methods

constructor ( props: object )

First things first. Call the constructor to instantiate World Speaker.

PropertyTypeOptionalityDefaultDescription
currentResourceIdstringNoA code which identifies the localization resource currently in use.
fallbackResourceIdstringYesundefinedA code which identifies the localization resource used if a string is missing in the resource currently in use.
markMissingbooleanYesfalseWraps a string in curly brackets if is missing both from the resource currently in use and, eventually, the fallback resource.
deferProcessingbooleanYestrueIf true, the resources are going to be processed not when added to the instance, but right before the first use.

Example:

const WorldSpeaker = require("world-speaker").WorldSpeaker;

const wsp = new WorldSpeaker({
    currentResourceId: 'en_GB',
    fallBackResourceId: 'it_IT',
    markMissing: true,
    deferProcessing: false
});

addResource ( props: object )

This method allows to add a localization resource to the instance. You have to assign an ID to the resource, then you can refer to it for any other method of World Speaker.

PropertyTypeOptionalityDefaultDescription
idstringNoA code which identifies the localization resource.
dataobjectNoAn collection on data where each key refers to a localized string.
typestringYes"direct"A word which identifies the kind of format used in the resource. It can be "direct","nested" or "custom".
convertfunctionYesundefinedThe function used in case of custom data time, that must return an object in the "direct" format.

Example:

wsp.addResource({
    id: 'it_IT',
    data: {
        "greetings": "Ciao mondo!",
        "niceToMeet": "Il mio nome è Federico, piacere di conoscerti!"
    }
});

wsp.addResource({
    id: 'en_GB',
    data: [
        {key: 'greetings', value: 'Hello world!'},
        {key: 'niceToMeet', value: "My name is Federico, nice to meet you!"},
        {key: 'phoneNumber', value: "I don't have a phone number..."}
    ],
    type: "custom",
    convert: (sourceData) => {
        let data = {};
        sourceData.forEach((element) => {
            if (!Object.keys(data).includes(element.key)) {
                data[element.key] = element.value;
            }
        });
        return data;
    }
});

We just added two custom resources to the instance, referred respectively with it_IT and en_GB in any other call. Since we set deferProcessing to false in the constructor, the convert function for en_GB resource will be called immediately. For it_IT resource we didn't need to provide any conversion function because the data format is implicitly "direct".

Now we provided proper resources both for the localization currently in use and for the fallback one.

setCurrentResourceId ( id: string )

Set the resource to be used from now on to the one referred by the given ID. Example:

wsp.setCurrentResourceId('it_IT');

From now on, the first attempt to look for a localized string will be on the it_IT localization resource.

setFallbackResourceId (id: string)

Set the resource to be used as fallback when a string isn't found in the currently used localization. Example:

wsp.setFallbackResourceId('en_GB');

From now on, whenever a strinc can't be found in the it_IT localization resource will bee searched in the en_GB localization resource.

speak ( domain: string, resourceId: string )

Attempt to find the localized string identified by the given domain in the current localization resource. If a resourceId is specified, the attempt will be done on that specific localization resource. If the attempt fails, the localized string will be searched in the fallback localization resource. If this attempt also fail, the method returns the domain itself.

Example:

                                                    // Outputs:
console.log( 'line 1 =', wsp.speak('greetings') );              // "line 1 = Ciao mondo!"
console.log( 'line 2 =', wsp.speak('niceToMeet') );             // "line 2 = Il mio nome è Federico, piacere di conoscerti!"
console.log( 'line 3 =', wsp.speak('greetings', 'en_GB') );     // "line 3 = Hello world!"
console.log( 'line 4 =', wsp.speak('niceToMeet', 'en_GB') );    // "line 4 = My name is Federico, nice to meet you!"
console.log( 'line 5 =', wsp.speak('phoneNumber') );            // "line 5 = I don't have a phone number..."
console.log( 'line 6 =', wsp.speak('emailAddress') );           // "line 6 = {{emailAddress}}"

On line 1 and 2, greetings and niceToMeet domains are both in the current localization resorce, that is it_IT. So, the output will be the italian localization. On line 3 and 4, the en_GB resource ID is specified, so the instance will look for the localization in the en_GB resource. On line 5 the instance attempts to look for the localization in the current resource, but phoneNumber do not exists on it_IT resource, so it attemps to look into the fallback resource, en_GB. The localization is there! On line 6, as the previous case, emailAddress doesn't exists in it_IT resource, but neither in en_GB resource. The instance simply returns the domain. Since we set markMissing to true in the constructor, the domain is wrapped in curly brackets.

_ ( domain: string)

Just an alias for speak method. Beware, because there is no way with this call to use a different resource than the current in use. Example:

console.log( wsp._('greetings') ); // Output: "Ciao mondo!"

Data formats

World Speaker supports two data formats, but providing a convert function to a resource it's basically capable of handle any kind of data collection, based on user needings. Let's see an example for each data format.

Format "direct"

This is the most common and easy format that World Speaker digest. It's just an object of keys, each one equal to a localized string.

const directFormatData = {
    "myForm.userInput.label": "User name or email",
    "myForm.userInput.placeholder": "firstname.lastname@domain.com",
    "myForm.passwordInput.label": "Your password",
    "myForm.passwordInput.placeholder": "Do not share your password with anybody"
};

It's worth saying that internally other formats are converted to this one, that's why the user custom conversion function has to the compliant to this format.

Format "nested"

This is the most useful format when handling bigger projects that requires some organization. The following data will be identical to the one in the direct example, but it's more human-readable and understandable - end even lighter.

const nestedFormatData = {
    myForm: {
        userInput: {
            label: "User name or email",
            placeholder: "firstname.lastname@domain.com"
        },
        passwordInput: {
            label: "Your password",
            placeholder: "Do not share your password with anybody"
        }
    }
};

Format "custom"

If your data source is compliant to any other kind of format, we just refer to it as "custom". A typical custom format may be the results fetched by a query to a MySQL database. The shape of those results is, most of the time, an array of objects. Let's say we have a table with our localizations with four columns.

idcontextkeyvalue
1myForm.userInputlabelUser name or email
2myForm.userInputplaceholderfirstname.lastname@domain.com
3myForm.passwordInputlabelYour password
4myForm.passwordInputplaceholderDo not share your password with anybody

We could also use a query to returns proper results in a easier shape, but let's just can handle the result of full select. Our results will be something like this:

const mysqlResultsFormatData = [
    { id: 1, context: "myForm.userInput", key: "label", value: "User name or email" },
    { id: 2, context: "myForm.userInput", key: "placeholder", value: "firstname.lastname@domain.com" },
    { id: 3, context: "myForm.passwordInput", key: "label", value: "Your password" },
    { id: 4, context: "myForm.passwordInput", key: "placeholder", value: "Do not share your password with anybody" }
];

Now we need a function to convert that format into the "direct" one. Les't do it!

const mysqlToWspConversion = (sourceData) => {
    let data = {};
    
    sourceData.forEach((element) => {
        const domain = `${element.context}.${element.key}`;
        if (!Object.keys(data).includes(domain)) {
            data[domain] = element.value;
        }
    });
    
    return data;
};

This function would give us data in "direct" format. We can just use that function before adding those data as resources or we can provide data as is and provide that function to the conversion property of the addResource method.

Some tips

Fallback localization as JSON module

If you are going to use World Speaker will be mostly for a node-like project, maybe React, Angular, Electron or what so ever. It's always a good thing to have at least the fallback resource as a static file in your project. The easy way to do that is a .json file in a resources folder, or something like that. Then you can import the json as a module (depends on the project configuration).

currentResourceId as state

If you use any kind of framework that uses states for their components, you could implement a state that stores the currentResourceId so the localization changes dynamically when the state changes. Let's do a little example in React and TypeScript.

const T = (props: {children: string}) => {
    return(
        <>{wsp.speak('props.children')}</>
    );
};

const App = () => {
    const [resourceId, setResourceId] = useState<string>('it_IT')
    wsp.setCurrentResourceId(resourceId);
    
    const changeResource = () => {
        setResourceId(
            resourceId === 'it_IT' ? 'en_GB' : 'it_IT'
        );
    };
    
    return(
        <div className={"App"}>
            <h1>My App</h1>
            <div>
                <T>some.kind.of.domain</T>
            </div>
            <div>
                {wsp._('some.kind.of.domain')}
            </div>
            <div>
                <button onClick={() => { changeResource(); }}>
                    Change resource
                </button>
            </div>
        </div>
    );
};

So, what is happening here? I created a component <T> to wrap the speak method. That's not mandatory, but it is just a great way to keep clean your React components. So in the <App> component I have two calls of speak. As you can see, I didn't provide a specific resource ID to the calls, so every call there will look for a localization in the resource currently in use.

At the top of the <App> component I used the state hook to store the initial value for the current localization resource id. Right after that I call setCurrentResourceId providing the state as argument. This means that every time the state changes, the currentResourceId will be set to the new resource ID. Since every call of speak needs to be updated, the component will be rendered again, showing the proper localization. That's it!

Of course, a better use for the localization would be an application context or a global application state.

Conclusions

I hope you can find this little library useful. There are tons of libraries for localization. This isn't the best (maybe...) nor the most complete. But it might be just the right one for you, and I enjoyed every moment spent on developing it.

As programmer you should always remember: you don't need to reinvent the wheel every time.

Unless it's fun. ;) Happy coding!

1.1.1

2 years ago

1.1.0

2 years ago

1.0.2

2 years ago

1.0.1

2 years ago

1.0.0

2 years ago